异步行为的焦点处理 - 不要窃取已经设置的焦点

时间:2013-11-06 00:17:09

标签: c# .net wpf asynchronous focus

[简介] 我有WPF应用示例。如果单击按钮,则会打开新窗口。我需要等待一段时间才能在其中加载数据。而不是被动地等待,我想在此期间做一些其他的事情。我可以打开一些上下文菜单。这种情况在下面的屏幕上说明:

enter image description here

这个等待我的窗口,在加载完成后(数据已准备好显示),会触发一个将焦点设置到网格的事件:

void DataLoaded(object sender, EventArgs e)
{             
    grid.Focus();
    grid.SelectedIndex = 0;
}

[当前问题] 不幸的是,在同一时刻,我们最近打开的上下文菜单刚刚消失。焦点被强行偷走了。恼人的最终效果如下所示:

enter image description here

[期望的效果] 什么是幸福的结局?如果用户只是将其更改为任何其他元素(如上下文菜单),则不会自动聚焦。换句话说 - 不要偷走焦点。

enter image description here

代码修改可能是:

void DataLoaded(object sender, EventArgs e)
{
    if (Magic.FocusNotChanged)
    {                
        grid.Focus();
        grid.SelectedIndex = 0;
    }
}

但是, Magic 是什么?一些全局发布订阅机制允许或拒绝自动焦点更改?一些暗中侦察的处理程序会改变吗?

BTW:上面显示的这个特定应用程序只是从更广泛的背景中人为地提取出来的。不要太在意这里实施的布局。必须发明一些通用机制,与此特定按钮或上下文菜单无关。有线索吗? 问候。

1 个答案:

答案 0 :(得分:0)

解决方案是平淡无奇的 - 以非侵入性方式关注焦点,检查窗口是否处于活动状态:

void DataLoaded(object sender, EventArgs e)
{
    if (this.IsActive)
    {                
        grid.Focus();
        grid.SelectedIndex = 0;
    }
}

进一步增强:

假设我们想要更通用的解决方案。如果我们想要从某个窗口托管的特定控件中设置焦点,而不是从窗口本身(缺少IsActive属性),该怎么办?我们需要找到它的父窗口,以检查它是否仍处于活动状态。更重要的是,让我们假设这个控件包含一堆子控件,我们希望将焦点设置为某个特定的子控件。看看这个:

void DataLoaded(object sender, EventArgs e)
{
    var window = this.GetParent<Window>();
    if (window.IsActive)
    {         
        var grid = this.GetChild<DataGrid>();
        grid.Focus();
    }
}

您可以看到两种辅助方法的用法。它们的实现如下:

public static T GetParent<T>(this DependencyObject child) where T : DependencyObject
{
    if (child == null) return null;

    // get parent item
    var parentObject = VisualTreeHelper.GetParent(child);
    // we’ve reached the end of the tree
    if (parentObject == null) return null;
    // check if the parent matches the type we’re looking for
    var parent = parentObject as T;
    // return parent if match or use recursion to proceed with next level
    return parent ?? GetParent<T>(parentObject);            
}

public static T GetChild<T>(this DependencyObject parent) where T : DependencyObject
{
    if (parent == null) return null;
    T result = null;

    var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
    for (var i = 0; i < childrenCount; i++)
    {
        var childObject = VisualTreeHelper.GetChild(parent, i);
        var child = childObject as T;
        if (child == null)
            result = childObject.GetChild<T>();
        else
        {
            result = (T) childObject;
            break;
        }
    }   
    return result;
}