WPF DatePicker LostFocus 触发七次
Posted
技术标签:
【中文标题】WPF DatePicker LostFocus 触发七次【英文标题】:WPF DatePicker LostFocus fires seven times 【发布时间】:2014-09-29 09:25:49 【问题描述】:我在这里有一个非常简单的场景。请看布局:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"></TextBox>
<DatePicker Grid.Row="1"
Name="_datePicker"
LostFocus="_datePicker_OnLostFocus"></DatePicker>
</Grid>
和代码隐藏:
private void _datePicker_OnLostFocus(object sender, RoutedEventArgs e)
Debug.WriteLine("LostFocuse");
所以,问题是当我选择某个日期并单击 TextBox
时,LostFocus
事件会触发 7(七!)次。当我点击TextBox
时,DatePicker
真的失去了焦点,剩下的六次对我来说完全无法解释。
我该如何解决?我只需要 一次 触发此事件。或者我可以使用其他活动吗?我尝试了LostKeyBoardFocus
,结果相同。
【问题讨论】:
您是否查看了调用堆栈以了解它是从哪里触发的? @Krishna 来自 WPF 系统中的某个位置。我的Window
里什么都没有。
你能告诉我们你想在 lostfocus 活动上做什么,或者你为什么选择这个活动?如果我们知道您的问题的背景,我们可以提出替代方案
@Krishna,我需要在DatePicker
失去焦点时只触发一次的任何事件。我选择LostFocus
事件,因为我认为它会给我带来那种行为。
【参考方案1】:
LostFocus 是路由策略设置为 Bubble
的路由事件。冒泡意味着它将冒泡到其父窗口直到根窗口,直到通过显式设置 e.Handled = true;
处理某处。
因此,这意味着即使 当子控件失去焦点时,它也会冒泡到您的 datePicker,这就是为什么您会看到多次点击您的方法。
您可以检查属性 IsKeyboardFocusWithin
如果焦点在您的控制范围内,则返回该属性。由于您对监听孩子失去焦点事件不感兴趣,因此您可以像这样在处理程序中检查此属性,并仅在 datePicker 失去实际焦点时执行您的代码:
private void _datePicker_OnLostFocus(object sender, RoutedEventArgs e)
DatePicker picker = sender as DatePicker;
if (!picker.IsKeyboardFocusWithin)
System.Diagnostics.Debug.WriteLine("LostFocuse");
【讨论】:
它有效,谢谢。但是如果这个事件是 bouble,你怎么看,为什么Sajeetharan
建议的解决方案不能正常工作?设置e.Handled = true;
必须停止冒泡,是吗?但实际上,事件不断冒泡,一次又一次地上升。
那是因为引发这个事件的不是一个孩子。在进程中,多个子控件引发事件,如 CalendarButton
等。您可以检查 e.OriginalSource
以查看是哪个子控件引发了事件。 Sajetharan
解决方案无论如何都不正确。当它已经到达 datePicker 父控件时,他正在设置 e.Handled = true
,它对事件引发没有任何改变。事件需要在子控件上标记为已处理。
嗯...我不太明白。我想如果我们有冒泡事件并在事件处理程序中设置e.Handled = true;
,这一定是停止冒泡的原因。正如您所说,处理程序在所有子控件上调用,e.Handled = true;
将在第一个事件上升时设置,并且它必须停止冒泡。我对此有点困惑。
@monstr - I thought if we have bubling event and set e.Handled = true; in event handler, this must be cause of stopping bubling up.
- 没错,但您需要在子控件上处理它,但根据 Sajeetharan 代码,他将其设置为在父控件上附加的处理程序上处理,这意味着它已经冒泡到现在的父母。要阻止它冒泡到父控件,您需要在子控件上附加事件并将其标记为已处理。
您可以通过遍历可视化树并将处理程序附加到每个控件,将处理程序从后面的代码附加到子控件。但我认为冒泡事件没有问题,因为您始终可以从控制实际引发事件的 OriginalSource 中过滤掉。【参考方案2】:
您可以添加一个 bool 值进行第一次检查,然后将 e.Handled 设置为 true。
bool isFired = false;
private void _datePicker_OnLostFocus(object sender, RoutedEventArgs e)
if (!isFired)
isFired = true;
e.Handled = true;
【讨论】:
它有效,但这种方法不会抑制事件触发。事件实际上也触发了 7 次。还有其他方法吗? @monstr 它不会触发 7 次,可能是您的代码的某些部分导致了这种情况。 我把所有的代码都贴出来了,我专门做了个空白窗口来测试。我尝试在代码隐藏中使用您的代码。处理程序调用六次(不是七次,你是对的:))。if(!isFired)
条件中的代码将被调用一次,但我强制使用附加标志 isFired
。我不喜欢我已经说过的那种方法。【参考方案3】:
您已经描述了UIElement.LostFocus
event 的正常.NET 行为。从 MSDN 上的链接页面:
当此元素失去逻辑焦点时发生。
注意它说逻辑焦点... WPF有两种焦点;逻辑和键盘。再次来自链接页面:
如果使用方法调用故意强制离开焦点但先前的键盘焦点存在于不同的范围内,则逻辑焦点不同于键盘焦点。在这种情况下,键盘焦点保持在原处,调用 Focus 方法的元素仍然获得逻辑焦点。
最后,为什么这个事件会被提出这么多次?这是因为即使DatePicker
中的子元素也可以在单击它们时从中移除逻辑焦点,并且随着焦点通过各种内部控件移动,它可以快速连续多次返回DatePicker
。再次,从链接页面:
由于此事件使用冒泡路由,失去焦点的元素可能是子元素,而不是实际附加事件处理程序的元素。
【讨论】:
感谢详细解释,但结果是,我必须使用DatePicker
的什么事件才能只有一次失焦触发?
没有这样的事件。以上是关于WPF DatePicker LostFocus 触发七次的主要内容,如果未能解决你的问题,请参考以下文章