当用户从组合框中选择值时,如果他们选择了一个值,则会触发“SelectionChanged”事件,并设置新值并且一切正常。但是,如果他们决定不更改值并单击UI上的其他位置(如他们想要编辑的文本框),则必须单击两次 - 第一次单击只需关闭组合框弹出窗口,下一次单击将关注他们想在第一次点击时激活的元素。
如何阻止组合框弹出窗口在第一次点击时劫持焦点目标?
我已经尝试过监控ComboBox_LostFocus事件,但这会在错误的时间触发。当用户单击下拉列表并显示弹出列表时,ComboBox_LostFocus事件将触发 - 它将失去焦点到它自己的下拉列表。我不想做任何改变。当用户点击然后弹出窗口关闭时,ComboBox永远不会重新获得焦点(焦点只是'丢失'到所有东西),所以这个事件没用。
答案 0 :(得分:6)
我想我可能找到了解决方案。组合框确实有一个DropDownClosed事件 - 问题是它不是RoutedEvent,所以你不能为ComboBox创建一个样式并让它们都通过EventSetter继承事件。 (您收到错误'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event"
)
但是,Loaded
事件是一个RoutedEvent,所以我们可以在样式中挂钩:
<Style x:Key="ComboBoxCellStyle" TargetType="ComboBox">
<EventSetter Event="Loaded" Handler="ComboBox_Loaded" />
</Style>
现在我们有一个事件总会在使用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();
}
}
我有一个ComboBox作为GridView中DataCell的模板,这个特殊问题阻止了DataRow在用户弹出打开ComboBox然后在网格外部单击时结束编辑。
这是我犯这个bug的最大问题。在此事件中设置焦点的第二个问题 iff 用户点击了。组合框也可能刚刚关闭,因为用户按Tab键或逃脱,所以我们不能只设置焦点到鼠标位置。我们需要有关导致DropDownClosed事件触发的更多信息。可能意味着在_Loaded事件处理程序中挂钩更多未被路由的事件。
答案 1 :(得分:2)
有一个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;
}