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

相关推荐
就是有点傻2 小时前
WPF中ImageBrush和Image的区别
wpf
冷眼Σ(-᷅_-᷄๑)2 小时前
WPF如何使用外部字体
wpf
月落.1 天前
WPF的<ContentControl>控件
wpf
就是有点傻1 天前
WPF中的依赖属性
开发语言·wpf
wangnaisheng1 天前
【WPF】把一个Window放在左上角/右上角顶格显示
wpf
WineMonk1 天前
.NET WPF CommunityToolkit.Mvvm框架
.net·wpf·mvvm
月落.1 天前
WPF中的INotifyPropertyChanged接口
wpf
界面开发小八哥1 天前
界面控件DevExpress WPF中文教程:Data Grid——卡片视图设置
.net·wpf·界面控件·devexpress·ui开发
平凡シンプル1 天前
WPF 打包
wpf
VickyJames1 天前
基于XAML框架和跨平台项目架构设计的深入技术分析
wpf·开源分享·unoplatform·winui3·项目架构