WPF TabControl使用时遇到的一些问题

近期遇到的不少问题都是由于TabControl以及DataGrid导致的,不能单独说这两个控件有BUG,但是组合使用时,确实会遇到不少奇怪的问题

框架版本依然是.NET472

切换TabItem导致光标自动被设置到了某个TextBox中去了

解决了上一篇遇到的DataGrid的问题之后,但是我的界面布局是TabControl-->TabItem-->DataGrid-->TextBox的结构,而我的TextBox则是DataGrid的最后一列,也是唯一一列可编辑列,

当我把所有的其他列都设置为ReadOnly之后,结果切换TabItem会导致焦点自动被最后一列的TextBox捕获,切换Tab后,TextBox自动进入编辑状态,而这是我不期望的情况

我判断这个是因为ReadOnly导致原本前面的单元格不再能够停靠焦点,因此尝试强制为前面的元素设置Focusable=True,发现能够解决这个问题了

第二种情况

解决了上面的问题之后,我了解导致TabItem的自动切换焦点机制,我发现我有很多其他地方也有类似问题,于是我都设置了一下Focusable,但是我在某个地方发现,这样的设置没有生效。

在一个TabItem-->(StackPanel1 , StackPanel2-->TextBox) 的这样的结构中,为第一个元素添加Focusable后,焦点依然被TextBox自动捕获了,

查询了一下,发现原来是TabIndex被设置成了3,TabIndex决定了用Tab进行焦点切换时的优先级,数字越小,优先级越高,默认是INT32_MAX,

于是,我将TabIndex相关设定删除后,发现果然解决了问题

TabItem的自动切换焦点也会按照TabIndex的属性来搜索当前子节点中优先级最高的节点来设置焦点

堆栈调试

我加入了GotFocus事件处理函数,来进行堆栈跟踪,下面就是截取的切换Tabitem时发生的函数调用,从中可以看到,发生了两次Focus操作,第一次Focus就是我从当前面板,点击了TabItem之后,焦点从当前位置切换到了TabItem之上

而当TabItem获得焦点之后,就立刻触发了它的默认处理函数,处理函数之中就会调用MoveFocus将焦点转移,而转移对象就是归属于目标TabItem的优先级最高的子元素

csharp 复制代码
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   在 System.Windows.FrameworkElement.OnGotFocus(RoutedEventArgs e)
   在 System.Windows.UIElement.IsFocused_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
   在 System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在 System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在 System.Windows.Controls.TextBox.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在 System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   在 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   在 System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   在 System.Windows.DependencyObject.SetValue(DependencyPropertyKey key, Object value)
   在 System.Windows.Input.FocusManager.OnFocusedElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   在 System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在 System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在 System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   在 System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   在 System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   在 System.Windows.Input.FocusManager.SetFocusedElement(DependencyObject element, IInputElement value)
   在 System.Windows.Input.KeyboardNavigation.UpdateFocusedElement(DependencyObject focusTarget)
   在 System.Windows.FrameworkElement.OnGotKeyboardFocus(Object sender, KeyboardFocusChangedEventArgs e)
   在 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   在 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   在 System.Windows.Input.InputManager.ProcessStagingArea()
   在 System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   在 System.Windows.Input.KeyboardDevice.ChangeFocus(DependencyObject focus, Int32 timestamp)
   在 System.Windows.Input.KeyboardDevice.TryChangeFocus(DependencyObject newFocus, IKeyboardInputProvider keyboardInputProvider, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed)
   在 System.Windows.Input.KeyboardDevice.Focus(DependencyObject focus, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed)
   在 System.Windows.Input.KeyboardDevice.Focus(IInputElement element)
   在 System.Windows.UIElement.Focus()
   在 System.Windows.Input.KeyboardNavigation.Navigate(DependencyObject currentElement, TraversalRequest request, ModifierKeys modifierKeys, DependencyObject firstElement, Boolean fromProcessInputTabKey)
   在 System.Windows.FrameworkElement.MoveFocus(TraversalRequest request)
   在 System.Windows.Controls.TabItem.OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
   在 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   在 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   在 System.Windows.Input.InputManager.ProcessStagingArea()
   在 System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   在 System.Windows.Input.KeyboardDevice.TryChangeFocus(DependencyObject newFocus, IKeyboardInputProvider keyboardInputProvider, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed)
   在 System.Windows.Input.KeyboardDevice.Focus(DependencyObject focus, Boolean askOld, Boolean askNew, Boolean forceToNullIfFailed)
   在 System.Windows.Input.KeyboardDevice.Focus(IInputElement element)
   在 System.Windows.UIElement.Focus()
   在 System.Windows.Controls.TabItem.SetFocus()
   在 System.Windows.Controls.TabItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)
   在 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
   在 System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)
   在 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   在 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   在 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   在 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   在 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   在 System.Windows.Input.InputManager.ProcessStagingArea()
   在 System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   在 System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   在 System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   在 System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   在 System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   在 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   在 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   在 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   在 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   在 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   在 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   在 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   在 System.Windows.Application.RunDispatcher(Object ignore)
   在 System.Windows.Application.RunInternal(Window window)

总结

处理了这几个问题之后,现在对于WPF对于焦点的处理有了更加深入的理解,理解之后,有很多奇怪的问题,也就变得合理了起来,由于在网上没有搜索到类似问题的资料,因此记录一下自己遇到的问题,和解决的方案和思路,方便自己,也为其他人提供一下相似问题的解决思路。

这是上一份DataGrid相关问题的文章链接
WPF DataGrid调试错误总结

相关推荐
分布式存储与RustFS4 小时前
告别复杂配置:用Milvus、RustFS和Vibe Coding,60分钟DIY专属Chatbot
wpf·文件系统·milvus·对象存储·minio·rustfs·vibe
攻城狮CSU16 小时前
WPF 绑定机制实现原理
wpf
攻城狮CSU16 小时前
WPF 之数据绑定一(Data Binding)
wpf
wuty0071 天前
记录一下 WPF进程 SendMessage 发送窗口消息进行进程间通信,存在进程权限无法接受消息的问题
wpf·进程间通信·sendmessage·进程权限
c#上位机2 天前
wpf之ToggleButton控件
c#·wpf
浪扼飞舟2 天前
WPF用户控件和依赖属性
wpf
c#上位机2 天前
wpf之MVVM中只读属性更新界面
c#·wpf·mvvm
就是有点傻2 天前
WPF自定义控件之环形进度条
wpf
He BianGu2 天前
【笔记】WPF中如何的动态设置DataGridTextColumn是否显示
笔记·wpf
Pota-to成长日记3 天前
Redisson 看门狗机制深度解析:分布式锁的守护者
分布式·wpf