简体   繁体   English

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

[英]How to get a combobox to appropriately set focus directly after the popup closes

When the user is selecting values from a combobox, if they choose a value, the "SelectionChanged" event fires and the new value is set and everything's fine. 当用户从组合框中选择值时,如果他们选择了一个值,则会触发“SelectionChanged”事件,并设置新值并且一切正常。 If, however, they decide not to change the value and click elsewhere on the UI (like a text box they want to edit), they have to click twice - the first click simply closes the combobox popup, and the next click will focus the element they wanted to activate on the first click. 但是,如果他们决定不更改值并单击UI上的其他位置(如他们想要编辑的文本框),则必须单击两次 - 第一次单击只需关闭组合框弹出窗口,下一次单击将关注他们想要在第一次点击时激活的元素。

How can I prevent the combobox popup from hijacking the focus target on the first click like that? 如何防止组合框弹出窗口在第一次点击时劫持焦点目标?

I've tried monitoring the ComboBox_LostFocus event, but this fires at the wrong time. 我已经尝试过监视ComboBox_LostFocus事件,但这会在错误的时间触发。 When the user clicks the dropdown and the popup list is displayed, the ComboBox_LostFocus event fires - it's losing focus to it's own dropdown list. 当用户单击下拉列表并显示弹出列表时,ComboBox_LostFocus事件将触发 - 它将失去焦点到它自己的下拉列表。 I don't want to do anything to change that. 我不想做任何改变。 When the user then clicks away and the popup closes, the ComboBox never regains focus (focus is just 'lost' to everything) and so this event is useless. 当用户点击然后弹出窗口关闭时,ComboBox永远不会重新获得焦点(焦点只是'丢失'到所有东西),因此这个事件是无用的。

I think I might have found a solution. 我想我可能找到了解决方案。 Comboboxes do have a DropDownClosed event - the problem is it isn't a RoutedEvent, so you can't create a style for ComboBoxes and have them all inherit the event via an EventSetter. 组合框确实有一个DropDownClosed事件 - 问题是它不是RoutedEvent,所以你不能为ComboBox创建一个样式并让它们都通过EventSetter继承事件。 (You get the error 'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event" ) (你得到的错误'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event"

However, the Loaded event is a RoutedEvent, so we can hook into that in the style: 但是, Loaded事件一个RoutedEvent,所以我们可以在样式中挂钩:

<Style x:Key="ComboBoxCellStyle" TargetType="ComboBox">
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded" />
</Style>

Now that we have an event that will always fire before anything else is done with the ComboBox, we can hook into the event we actually care about: 现在我们有一个事件总是会在ComboBox完成任何其他事情之前触发,我们可以挂钩到我们真正关心的事件:

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

Now that I finally have access to the event that fires when the DropDown is closing, I can perform whatever actions I need to make sure the focus is terminated on the bothersome ComboBox. 现在我终于可以访问DropDown关闭时触发的事件,我可以执行我需要的任何操作,以确保焦点在麻烦的ComboBox上终止。 In my case, the following: 在我的情况下,以下内容:

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();
    }
}

I had a ComboBox as the template of a DataCell in a GridView, and this particular problem was preventing the DataRow from ending edit when the user popped open a ComboBox then clicked outside of the grid. 我有一个ComboBox作为GridView中DataCell的模板,这个特殊问题阻止了DataRow在用户弹出打开ComboBox然后在网格外部单击时结束编辑。

That was my biggest problem with this bug. 这是我犯这个bug的最大问题。 A secondary problem setting the focus in this event iff the user clicked. 如果用户单击,则在此事件中设置焦点的次要问题。 The combobox might also have just been closed because the user hit tab or escape though, so we can't just setfocus to the mouse position. 组合框也可能刚刚关闭,因为用户按Tab键或逃脱,所以我们不能只设置焦点到鼠标位置。 We'd need more information on what caused the DropDownClosed event to fire. 我们需要有关导致DropDownClosed事件触发的更多信息。 Probably means hooking into more unrouted events in the _Loaded event handler. 可能意味着在_Loaded事件处理程序中挂钩更多未被路由的事件。

There's a DropDownClosed event: 有一个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();
}

This will only set focus to whatever control they clicked on. 这只会将焦点设置为他们点击的任何控件。 If they click a TextBox, for instance, the caret will be at the left rather than where they clicked. 例如,如果它们单击TextBox,则插入符号将位于左侧而不是它们单击的位置。 If they click another ComboBox, it'll focus there, but it won't show its popup. 如果他们点击另一个ComboBox,它会集中在那里,但它不会显示它的弹出窗口。 However, I'm sure you could deal with those cases in this event handler if you need to. 但是,如果您需要,我确信您可以在此事件处理程序中处理这些情况。

EDIT: Whoops, you're using WPF! 编辑:哎呀,你正在使用WPF! Nevermind, then; 没关系; this is how you'd do it in WinForms. 这就是你在WinForms中的表现。 However, you've still got the DropDownClosed event in WPF. 但是,您仍然在WPF中获得了DropDownClosed事件。

EDIT 2: This seems to do it. 编辑2:这似乎是这样做的。 I'm not familiar with WPF so I don't know how robust it is, but it'll focus on a TextBox, for example. 我不熟悉WPF,所以我不知道它有多强大,但它会专注于一个TextBox,例如。 This is a default WPF app with a Window called MainWindow. 这是一个默认的WPF应用程序,其窗口名为MainWindow。 When you close the DropDown of the comboBox, it'll focus the top-most focusable Control at the mouse position that isn't 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM