这是项目里的一个问题点,组里的同学在设置Focus事件时候,遇到了死锁,最后StackOverFlow。
写一个小例子
一个Textbox控件输入值,一个TextBlock显示结果,一个检索Button
TextBox框里输入一个ID,去系统里检索一个值,把结果显示在TextBlock里。
当查询不到结果时候把焦点停留在TextBox里,当然触发的条件有TextBox失去焦点时
和按检索Button时。对于测试焦点的小例子,我们当然不用去连数据库,写个简单的方法
如果输入的值为【123】时候返回结果AAA,输入其他以外的结果表示不存在,把焦点停留在
Textbox里
很多没有同学开始写的时候都会Button_Click方法和Textbox_LostFocus方法去实现自己
的代码,Button_Click事件一点问题也没有,但是LostFocus时候却反生了异常终了,百思不得其解。
今天我们就来解决2个问题,为什么会不行?该怎么解决?
【原因】
请看一段
Private Shared Sub IsFocused_Changed(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs) Dim source As UIElement = DirectCast(d, UIElement) If CBool(e.NewValue) Then source.OnGotFocus(New RoutedEventArgs(UIElement.GotFocusEvent, source)) Else source.OnLostFocus(New RoutedEventArgs(UIElement.LostFocusEvent, source)) End If End Sub Public ReadOnly Property IsFocused As Boolean Get Return CBool(MyBase.GetValue(UIElement.IsFocusedProperty)) End Get End Property Friend Shared ReadOnly IsFocusedPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("IsFocused", GetType(Boolean), GetType(UIElement), New PropertyMetadata(BooleanBoxes.FalseBox, New PropertyChangedCallback(AddressOf UIElement.IsFocused_Changed))) Public Custom Event LostFocus As RoutedEventHandler AddHandler(ByVal value As RoutedEventHandler) Me.AddHandler(UIElement.LostFocusEvent, value) End AddHandler RemoveHandler(ByVal value As RoutedEventHandler) Me.RemoveHandler(UIElement.LostFocusEvent, value) End RemoveHandler End Event
LostFocus事件没有前置事件,所以不能e.Handled=True去控制,所以他被设置成一个只能执行的方法,拥有一个IsFocused的依赖属性,当我们在写代码时候
前置写方法Textbox1.Focus()时候,就造成LostFoucs事件没有结束,还不能结束,进程互相等待,最后StackOverFLow
【解决方法】
1.替换事件
改用LostKeyBoardFocus事件。这个是在System.Windows.Input中完全是对于键盘事件的方法。当焦点完全离开对象时候,该事件才被触发
所以把LostFocus的处理写在LostKeyBoardFocus里就能解决上述问题
[SecurityCritical] private static void OnLostKeyboardFocusThunk(object sender, KeyboardFocusChangedEventArgs e) { Invariant.Assert(!e.Handled, "Unexpected: Event has already been handled."); UIElement uiElement = sender as UIElement; if (uiElement != null) { uiElement.OnLostKeyboardFocus(e); } else { ContentElement contentElement = sender as ContentElement; if (contentElement != null) contentElement.OnLostKeyboardFocus(e); else ((UIElement3D) sender).OnLostKeyboardFocus(e); } }
2.使用Dispatcher.BeginInvoke用异步的方法来更新
把更新Focus这件事放入UI进程的队列中,等待前一处理结束后,进行设置
Private Sub TextBox1_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles TextBox1.LostFocus If Trim(TextBox1.Text).Equals("123") Then TextBlock2.Text = "AAA" Else TextBlock2.Text = String.Empty Me.Dispatcher.BeginInvoke(DispatcherPriority.Render, New Action(Sub() TextBox1.Focus())) End If End Sub