.net 在WinForms应用程序中查找焦点控件的首选方法是什么?

.net 在WinForms应用程序中查找焦点控件的首选方法是什么?,.net,winforms,.net,Winforms,查找当前正在WinForms中接收用户(键盘)输入的控件的首选/最简单方法是什么 到目前为止,我已经得出以下结论: public static Control FindFocusedControl(Control control) { var container = control as ContainerControl; return (null != container ? FindFocusedControl(container.ActiveControl)

查找当前正在WinForms中接收用户(键盘)输入的控件的首选/最简单方法是什么

到目前为止,我已经得出以下结论:

public static Control FindFocusedControl(Control control)
{
    var container = control as ContainerControl;
    return (null != container
        ? FindFocusedControl(container.ActiveControl)
        : control);
}
从表单中,这可以简单地称为(在.NET3.5+中,这甚至可以定义为表单上的扩展方法)-

这是否恰当

是否有一个内置的方法,我应该用它来代替

请注意,使用层次结构时,仅调用ActiveControl是不够的。想象一下:

Form
    TableLayoutPanel
        FlowLayoutPanel
            TextBox (focused)

(formInstance)。ActiveControl将返回对TableLayoutPanel的引用,而不是文本框(因为在我查找叶控件时,ActiveControl似乎只返回控件树中的立即活动子控件)。

搜索Internet后,我在

.Net framework库不提供要查询的API 聚焦控制。你必须 调用windows API以执行此操作:

[C#]


如果您递归地跟踪ActiveControl,它不会将您带到具有焦点的叶控件?

如果您已经对Windows API进行了其他调用,那么使用Peters解决方案没有什么害处。但我理解您对此的担忧,并且倾向于使用与您类似的解决方案,只使用框架功能。毕竟,性能差异(如果有的话)不应该很大

我将采用非递归方法:

public static Control FindFocusedControl(Control control)
{
    var container = control as IContainerControl;
    while (container != null)
    {
        control = container.ActiveControl;
        container = control as IContainerControl;
    }
    return control;
}

Hinek的解决方案对我来说效果很好,只是它是ContainerControl,而不是ControlContainer。(以防万一你被那条红色的蜿蜒线弄伤了头。)


窗体或容器上的ActiveControl将返回该实体的活动控件,无论它嵌套在其他容器中的深度如何

在您的示例中,如果文本框具有焦点:那么:对于表单、TableLayoutPanel和FlowLayoutPanel:所有这些的“ActiveControl”属性都将是文本框

一些,但不是全部,“真正的”集装箱控制类型。。。像表单和用户控件。。。公开键事件(在Form的情况下:只有当Form.KeyPreview==true时,才能使用它们)

根据设计,包含其他控件(如TableLayOutPanel、GroupBox、Panel、FlowLayoutPanel等)的其他控件是类型的ContainerControl,并且它们不公开KeyEvents

任何将TextBox、FlowLayoutPanel、TableLayoutPanel等对象的实例直接强制转换到ContainerControl的尝试都不会编译:它们不是ContainerControl类型

接受答案中的代码,以及更正第一个答案拼写错误的下一个答案中的代码,将编译/接受上述实例作为参数,因为您通过设置参数类型控件将它们“向下转换”为类型“Control”

但在每种情况下,转换到ControlContainer将返回null,传入的实例将返回(向下转换):本质上是一个no-op

是的,如果您将一个“真正的”ControlContainer(如表单实例)传递给修改后的应答代码,它将工作,它位于ActiveControl的父继承路径中,但您仍然在浪费时间复制“ActiveControl”的功能

那么什么是“真正的”集装箱控制:请查看:

只有Peter的答案才真正回答了这个明确的问题,但这个答案带来了使用互操作的代价,并且“ActiveControl将为您提供您所需要的”

还要注意的是,每个控件(容器或非容器)都有一个从不为null的控件集合,而且很多基本WinForms控件(我从来没有尝试过所有控件:为什么我会?)允许您执行“疯狂的操作”,例如将控件添加到“简单”控件(如按钮)的控件集合中而不会出错

现在,如果你的问题的真正目的是问你如何找到最外层的集装箱控制。。。那不在表格上。。。一个常规的非容器控件嵌套了一些任意级别的深。。。您可以使用答案中的一些想法:但是代码可以大大简化

常规控件、ContainerControl、UserControls等(但不是表单!)都有一个“Container”属性,您可以访问该属性来获取它们的直接容器,但要确保在它们的固有路径中有“final Container”,而该路径不是表单,需要一些代码来“遍历”继承树,这里演示了这一点

您可能还希望查看控件的“HasChildren属性”,该属性通常用于处理WinForms中的焦点、ActiveControl和Select问题。在这里,回顾Select和Focus之间的差异是很有价值的,因此在这方面有一些很好的资源


希望这能有所帮助。

ActiveControl并不总是有效,就像SplitContainer一样,ActiveControl.Focused是假的

因此,对于更简单的方法,可以这样做:

private IEnumerable<Control> _get_all_controls(Control c)
{
    return c.Controls.Cast<Control>().SelectMany(item =>
        _get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
        control.Name != string.Empty);
}

var _controls = _get_all_controls(this);
foreach (Control control in _controls) 
    if (control.Focused)
    {
        Console.WriteLine(control.Name);
        break;
    }
private IEnumerable\u get\u all\u控件(控件c)
{
返回c.Controls.Cast().SelectMany(项=>
_获取所有控件(项)).Concat(c.controls.Cast())。其中(控件=>
控件名!=string.Empty);
}
var\u controls=\u获取所有\u控件(此);
foreach(控制中的控制_控制)
if(control.Focused)
{
Console.WriteLine(control.Name);
打破
}

使用互操作调用会比使用诸如ActiveControl方法之类的内置功能更可取吗?这种解决方案的代码访问安全性分支如何?甚至在Vista API中,只要使用.NET就可以实现所有功能,例如,尝试更改Vista下progressbar的颜色。只有3种颜色可用(状态:错误、警告、默认),您只能通过API调用进行更改。或者玻璃效果…我已经尝试了上面的代码,但无法在第10行中完善“GetFocus()”方法。这非常有效!不知道为什么递归FindFocusedControl被标记为接受答案,它对我不起作用,总是返回null。这就是func
public static Control FindFocusedControl(Control control)
{
    var container = control as IContainerControl;
    while (container != null)
    {
        control = container.ActiveControl;
        container = control as IContainerControl;
    }
    return control;
}
    public static Control FindFocusedControl(Control control)
    {
        ContainerControl container = control as ContainerControl;
        while (container != null)
        {
            control = container.ActiveControl;
            container = control as ContainerControl;
        }
        return control;
    }
private IEnumerable<Control> _get_all_controls(Control c)
{
    return c.Controls.Cast<Control>().SelectMany(item =>
        _get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
        control.Name != string.Empty);
}

var _controls = _get_all_controls(this);
foreach (Control control in _controls) 
    if (control.Focused)
    {
        Console.WriteLine(control.Name);
        break;
    }