近期遇到的不少问题都是由于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调试错误总结