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调试错误总结

相关推荐
FuckPatience1 小时前
WPF 改变根据某一列条件改变行样式
wpf
dalong1017 小时前
WPF:DataGrid样式
wpf
samble3 天前
从零搭建工业控制系统(二十二):线程安全与并发控制
c#·wpf·并发·mvvm·线程安全·工业控制
姜穆澜3 天前
OneID 从 0 到 1 完整生产案例(三)
大数据·wpf
dalong103 天前
WPF:MVVM 示例
大数据·wpf
samble3 天前
从零搭建工业控制系统(二十一):状态管理系统——中心状态对象设计
c#·wpf·mvvm·状态管理·工业控制
mengge.cloud3 天前
存储技术基础小白教程
linux·运维·服务器·wpf·存储
DreamLife☼4 天前
复杂Agent系统架构设计
系统架构·wpf·agent·组件·设计·构架·说明
SamChan904 天前
PDF翻译服务端到端基准测试:4款主流方案的吞吐量、延迟、内存占用对比
服务器·网络·python·ai·pdf·wpf
bugcome_com4 天前
Avalonia 控件模板实战:从 WPF 迁移自定义 Button 样式的完整指南
wpf