在 WinForms 应用程序中找到集中控件的首选方法是什么?

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

到目前为止,我想到了以下几点:

public static Control FindFocusedControl(Control control)
{
var container = control as ContainerControl;
return (null != container
? FindFocusedControl(container.ActiveControl)
: control);
}

从表单中,这可以简单地称为(在。NET 3.5 + 这甚至可以定义为表单上的扩展方法)-

var focused = FindFocusedControl(this);

这样合适吗?

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

请注意,在使用层次结构时,仅调用 ActiveControl 是不够的:

Form
TableLayoutPanel
FlowLayoutPanel
TextBox (focused)

(表格格式)。ActiveControl 将返回对 TableLayoutPanel 的引用,而不是 TextBox (因为 ActiveControl 似乎只返回控件树中的即时活动子节点,而我正在寻找叶子控件)。

66280 次浏览

在互联网上搜索之后,我在 George Shepherd 的 Windows 窗体常见问题解答上找到了以下内容

Net 框架库不提供要查询的 API 集中控制,你必须这么做 调用一个 windows API 来实现这一点:

[ C # ]

public class MyForm : Form
{
[DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]
internal static extern IntPtr GetFocus();


private Control GetFocusedControl()
{
Control focusedControl = null;
// To get hold of the focused control:
IntPtr focusedHandle = GetFocus();
if(focusedHandle != IntPtr.Zero)
// Note that if the focused Control is not a .Net control, then this will return null.
focusedControl = Control.FromHandle(focusedHandle);
return focusedControl;
}
}

如果你递归地跟踪 ActiveControl,它不会带你到有焦点的叶子控件吗?

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

我会采取非递归的方法:

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

Hinek 的解决方案很适合我,除了它是 集装箱控制中心,而不是 ControlContainer。(以防你对那条红色的弯弯曲曲的线感到头疼。)

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

Form 或 Container 威尔上的 ActiveControl 返回该实体的活动控件,无论它可能嵌套在其他容器中多深。

在您的示例中,如果 TextBox 具有 Focus: 那么: for Form,TableLayoutPanel 和 FlowLayoutPanel: ‘ ActiveControl 属性 他们所有人将是 TextBox!

有些(但不是全部)“真正的”ContainerControl 类型... ... 如 Form 和 UserControl... ... 公开 Key Events (在 Form 的情况下: 仅当 Form。KeyPreview = = true 可以使用)。

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

任何将诸如 TextBox、 FlowLayoutPanel、 TableLayoutPanel 直接之类的对象转换为 ContainerControl 的尝试都将无法编译: 它们不是 ContainerControl 类型。

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

但是在每种情况下,ControlContainer 的强制转换都将返回 null,并且实例中传递的内容将返回(强制转换) : 本质上是一个 no-op。

而且,是的,修改后的应答代码将会工作,如果你传递给它一个“真正的”ControlContainer,就像一个 Form 实例,它在 ActiveControl 的父继承路径中,但是你仍然在浪费时间复制‘ ActiveControl’的函数。

那么什么是“真正的”容器控件: 检查它们: 集装箱控制的 MS 文档

只有 Peter 的答案真正回答了这个明确的问题,但是这个答案带有使用互操作的代价,而且 ActiveControl 会给你你需要的东西。

还要注意的是,每个 Control (容器或非容器)都有一个从不为空的 Controls Collection,而且有很多(我从未尝试过所有这些集合: 为什么要尝试?)基本的 WinForms 控件可以让你做一些“疯狂的事情”,比如将 Controls 添加到像 Button 这样的“简单”控件的 ControlCollection 中,而不会出现错误。

现在,如果你的问题的 真正的目的是问你如何找到最外层的容器控制 ... ... 不在表格上... ... 的 普通非集装箱货柜控制嵌套一些任意深度... ... 你可以使用一些 想法在答案: 但代码可以大大简化。

常规控件、 ContainerControls、 UserControls 等(但不包括 Form!)都有一个‘ Container 属性,您可以访问它们的即时容器,但是确保在它们的继承路径中有‘ final Container,这不是 Form,需要一些代码来“遍历”继承树,如下所示。

您可能还希望查看“ Control”的“ HasChildren”属性,该属性通常在处理 Windows 窗体中的 Focus、 ActiveControl 和 Select 问题时非常有用。回顾 Select 和 Focus 之间的区别在这里很有价值,SO 在这方面有一些很好的资源。

希望这个能帮上忙。

ActiveControl 并不总是正常工作,就像 SplitContainer、 ActiveControl 一样。 Focus 是 false。

因此,对于一个更加愚蠢的方法,可以这样做:

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