【问题标题】:Focus handling for async behaviour - do not steal already set focus异步行为的焦点处理 - 不要窃取已经设置的焦点
【发布时间】:2013-11-08 18:10:37
【问题描述】:

[介绍] 我有示例 WPF 应用程序。如果我单击一个按钮,则会打开新窗口。我需要等待一段时间才能在其中加载数据。与其被动地等待,我还想做一些其他的事情。例如,我可以打开一些上下文菜单。这种情况如下图所示:

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

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

[当前问题]不幸的是,就在同一时刻,我们最近打开的上下文菜单刚刚消失。焦点已被强行窃取。烦人的最终效果如下图:

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

代码修改可以是:

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

但是,魔法是什么?一些允许或拒绝自动焦点更改的全局发布订阅机制?一些正在监视焦点变化的处理程序?

顺便说一句:上面显示的这个特定应用程序只是从更广泛的上下文中人为提取的。不要太注意这里实现的布局。必须发明一些与此特定按钮或上下文菜单无关的通用机制。有什么线索吗? 问候。

【问题讨论】:

  • 唯一让我想到的是在处理加载数据的事件处理程序时检查当前焦点是否仍在按钮上。假设这是“用户没有进行其他焦点操作”
  • @Marvin Smit:我认为这不是一个选择。当事件处理程序被触发时,空窗口已经打开了一段时间。所以焦点不再存在于按钮上。据我了解,它早已不复存在。更重要的是,一个按钮只是一个例子。可以通过其他方式打开窗口,例如通过上下文菜单,在选择菜单项后立即消失。

标签: c# .net wpf asynchronous focus


【解决方案1】:

解决方案是平淡无奇的 - 以非侵入性方式集中注意力,检查当时窗口是否处于活动状态:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多