如何在弹出窗口关闭后直接使用combobox来正确设置焦点

当用户从combobox中选择值时,如果他们选择了一个值,则会触发“SelectionChanged”事件并设置新值并且一切正常。 但是,如果他们决定不更改值并单击UI上的其他位置(如他们要编辑的文本框),则必须单击两次 – 第一次单击只需关闭combobox弹出窗口,然后下一次单击将关注他们想要在第一次点击时激活的元素。

如何阻止combobox弹出窗口在第一次点击时劫持焦点目标?

我已经尝试过监视ComboBox_LostFocus事件,但这会在错误的时间触发。 当用户单击下拉列表并显示弹出列表时,ComboBox_LostFocus事件将触发 – 它将失去焦点到它自己的下拉列表。 我不想做任何改变。 当用户点击然后弹出窗口关闭时,ComboBox永远不会重新获得焦点(焦点只是’丢失’到所有东西),因此这个事件是无用的。

我想我可能找到了解决方案。 combobox确实有一个DropDownClosed事件 – 问题是它不是RoutedEvent,所以你不能为ComboBox创建一个样式并让它们都通过EventSetterinheritance事件。 (你得到的错误'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event"

但是, Loaded事件一个RoutedEvent,所以我们可以在样式中挂钩:

  

现在我们有一个事件总是会在ComboBox完成之前触发,我们可以挂钩到我们真正关心的事件:

 private void ComboBox_Loaded(object sender, RoutedEventArgs e) { ((ComboBox)sender).DropDownClosed -= ComboBox_OnDropDownClosed; ((ComboBox)sender).DropDownClosed += new System.EventHandler(ComboBox_OnDropDownClosed); } 

现在我终于可以访问DropDown关闭时触发的事件,我可以执行我需要的任何操作,以确保焦点在麻烦的ComboBox上终止。 在我的情况下,以下内容:

 void ComboBox_OnDropDownClosed(object sender, System.EventArgs e) { FrameworkElement visualElement = (FrameworkElement)sender; while( visualElement != null && !(visualElement is DataCell) ) visualElement = (FrameworkElement)visualElement.TemplatedParent; if( visualElement is DataCell ) { DataCell dataCell = (DataCell)visualElement; dataCell.EndEdit(); if( !(dataCell.ParentRow is InsertionRow) ) dataCell.ParentRow.EndEdit(); } } 

我在GridView中有一个ComboBox作为DataCell的模板,这个特殊的问题阻止了当用户弹出一个ComboBox然后在网格外部点击时DataRow结束编辑。

这是我犯这个bug的最大问题。 如果用户单击,则在此事件中设置焦点的次要问题。 combobox也可能刚刚关闭,因为用户按Tab键或逃脱,所以我们不能只设置焦点到鼠标位置。 我们需要有关导致DropDownClosed事件触发的更多信息。 可能意味着在_Loaded事件处理程序中挂钩更多未被路由的事件。

有一个DropDownClosed事件:

 private void comboBox_DropDownClosed(object sender, EventArgs e) { Point m = Control.MousePosition; Point p = this.PointToClient(m); Control c = this.GetChildAtPoint(p); c.Focus(); } 

这只会将焦点设置为他们点击的任何控件。 例如,如果它们单击TextBox,则插入符号将位于左侧而不是它们单击的位置。 如果他们点击另一个ComboBox,它会集中在那里,但它不会显示它的弹出窗口。 但是,如果您需要,我相信您可以在此事件处理程序中处理这些情况。

编辑:哎呀,你正在使用WPF! 没关系; 这就是你在WinForms中的表现。 但是,您仍然在WPF中获得了DropDownClosed事件。

编辑2:这似乎做到了。 我不熟悉WPF,所以我不知道它有多强大,但它会专注于TextBox,例如。 这是一个默认的WPF应用程序,其窗口名为MainWindow。 当你关闭comboBox的DropDown时,它会将最顶端的可聚焦控件聚焦在非MainWindow的鼠标位置:

 private void comboBox_DropDownClosed(object sender, EventArgs e) { Point m = Mouse.GetPosition(this); VisualTreeHelper.HitTest(this, new HitTestFilterCallback(FilterCallback), new HitTestResultCallback(ResultCallback), new PointHitTestParameters(m)); } private HitTestFilterBehavior FilterCallback(DependencyObject o) { var c = o as Control; if ((c != null) && !(o is MainWindow)) { if (c.Focusable) { c.Focus(); return HitTestFilterBehavior.Stop; } } return HitTestFilterBehavior.Continue; } private HitTestResultBehavior ResultCallback(HitTestResult r) { return HitTestResultBehavior.Continue; }