C# 如何让组合框在弹出窗口关闭后直接适当设置焦点

C# 如何让组合框在弹出窗口关闭后直接适当设置焦点,c#,wpf,xaml,.net-4.0,C#,Wpf,Xaml,.net 4.0,当用户从组合框中选择值时,如果他们选择了一个值,“SelectionChanged”事件将触发,并设置新值,一切正常。但是,如果他们决定不更改该值并单击UI上的其他位置(如他们想要编辑的文本框),他们必须单击两次-第一次单击仅关闭组合框弹出窗口,下一次单击将在第一次单击时聚焦他们想要激活的元素 如何防止组合框弹出窗口在第一次单击时劫持焦点目标 我尝试监视ComboBox\u LostFocus事件,但这在错误的时间触发。当用户单击下拉列表并显示弹出列表时,将触发ComboBox_LostFocu

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

如何防止组合框弹出窗口在第一次单击时劫持焦点目标


我尝试监视ComboBox\u LostFocus事件,但这在错误的时间触发。当用户单击下拉列表并显示弹出列表时,将触发ComboBox_LostFocus事件-它将失去对自己下拉列表的关注。我不想做任何改变。当用户单击离开并关闭弹出窗口时,组合框将永远无法恢复焦点(焦点只是“丢失”了所有内容),因此此事件是无用的。

有一个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();
}
这只会将焦点设置为他们单击的任何控件。例如,如果他们单击文本框,插入符号将位于左侧,而不是单击的位置。如果他们单击另一个组合框,它将在那里聚焦,但不会显示其弹出窗口。但是,如果需要,我相信您可以在此事件处理程序中处理这些情况

编辑:哎呀,你在用WPF!那就别管了;这就是在WinForms中执行此操作的方式。但是,在WPF中仍然有DropDownClosed事件

编辑2:这似乎可以做到。我不熟悉WPF,所以我不知道它有多健壮,但它会关注文本框,例如。这是一个默认的WPF应用程序,有一个名为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;
}

我想我可能已经找到了解决办法。组合框确实有一个DropDownClosed事件-问题是它不是RoutedEvent,所以不能为组合框创建样式并让它们通过EventSetter继承事件。(出现错误时,
“DropDownClosed”必须是注册的RoutedEvent,其名称以关键字“Event”结尾。

但是,
Loaded
事件是一个RoutedEvent,因此我们可以在样式中连接到它:

<Style x:Key="ComboBoxCellStyle" TargetType="ComboBox">
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded" />
</Style>
现在我终于可以访问下拉菜单关闭时触发的事件了,我可以执行我需要的任何操作,以确保焦点在烦人的组合框上终止。就我而言,以下是:

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作为数据单元的模板,当用户打开一个ComboBox然后在网格外单击时,这个特殊的问题阻止了DataRow结束编辑


这是我遇到的最大问题。第二个问题是在用户单击时设置此事件中的焦点。组合框也可能刚刚关闭,因为用户点击了tab或escape,所以我们不能将焦点设置为鼠标位置。我们需要更多关于是什么导致DropDownClosed事件触发的信息。可能意味着在加载的事件处理程序中挂接更多未路由的事件。

Hi@Alain,这里的问题是您试图偏离标准控件的常规行为。。即使你认为按照你描述的方式做会更好,但这与人们习惯组合框工作的方式不一致,在大多数情况下,这是一个坏主意。这里的真正问题是用户抱怨他们“需要在查看组合框列表后UI响应之前单击两次”。如果我回到用户那里,告诉他们这是设计,他们会找到另一个设计师。这就是为什么我在大多数情况下说。。。因为我们以前都必须和客户打交道。。。希望一些WPF大师能帮上忙!我会尝试两件事:第一,尝试侵入
组合框
,看看你是否能进入它的内部
弹出窗口
。如果没有,请扩展组合框,并实现相应的功能。这可能需要一些
鼠标
黑客攻击。如果你找到一个解决方案,请发布一个解决方案..努力点,老兄,说真的。
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();
    }
}