在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)
(formInstance).ActiveControl将返回对TableLayoutPanel的引用,而不是TextBox(因为ActiveControl似乎只是在控制树中返回直接活动的子节点,而我正在寻找叶子控件)。
答案 0 :(得分:64)
如果您已经对Windows API进行了其他调用,那么使用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;
}
答案 1 :(得分:25)
在搜索互联网后,我在George Shepherd's Windows Forms FAQ
上找到了以下内容.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;
}
}
答案 2 :(得分:20)
表单或容器上的ActiveControl将返回该实体的主动控件,无论它嵌套在其他容器中的深度如何。
在您的示例中,如果TextBox具有Focus:则:对于Form,TableLayoutPanel和FlowLayoutPanel:所有这些的
一些(但不是全部)“真正的”ContainerControl类型......比如Form和UserControl ...公开关键事件(在Form的情况下:仅当Form.KeyPreview == true才可以使用它们)。
其他控件,根据设计,包含其他控件,如TableLayOutPanel,GroupBox,Panel,FlowLayoutPanel等,不类型为ContainerControl,它们不会公开KeyEvents。
任何将TextBox,FlowLayoutPanel,TableLayoutPanel 等对象实例强制转换为ContainerControl的尝试都不会编译:它们不是类型ContainerControl。
接受的答案中的代码,以及纠正第一个答案的拼写错误的下一个答案,将编译/接受上述实例作为参数,因为您“向下转换”它们以键入'通过制作参数类型进行控制'控制
但是在每种情况下,对ControlContainer的强制转换将返回null,并且将返回传入的实例(下载):基本上是无操作。
并且,是的,如果你传递一个“真正的”ControlContainer,就像一个Form实例,它在ActiveControl的父继承路径中,修改后的答案代码将会起作用,但你仍然只是浪费时间重复功能“ACTIVECONTROL。
那么什么是“真正的”ContainerControls:检查出来:MS docs for ContainerControl
只有彼得的答案真的回答了明确的问题,但这个答案带有使用互操作的代价,而'ActiveControl会给你你所需要的。
另请注意,每个Control(容器或非容器)都有一个永远不会为null的Controls Collection,而且很多(我从未尝试过所有这些:我为什么会这样?)基本的WinForms控件让你做“疯狂的东西”,比如将控件添加到像Button这样的“简单”控件的ControlCollection而没有错误。
现在,如果您的问题的真实意图是询问您如何找到不在表单本身上的最外层ContainerControl ... ... 常规非容器控件嵌套了一些任意级别...你可以在答案中使用一些想法:但代码可以很大简化。
Regular Controls,ContainerControls,UserControls等(但不是Form!)都有一个'容器属性,你可以访问它来获取它们的直接容器,但确保你的inhertance路径中的'最终容器不是一个Form需要一些代码来“继续”继承树,这将在这里演示。
您可能还希望查看'HasChildren属性'控件,它通常用于处理WinForms中的Focus,ActiveControl和Select问题。回顾Select和Focus之间的区别在这里很有价值,而且SO有一些很好的资源。
希望这有帮助。
答案 3 :(得分:8)
Hinek的解决方案对我来说很有效,除了它是 ContainerControl ,而不是ControlContainer。 (以防万一你正在摸着那条红色的波浪线。)
public static Control FindFocusedControl(Control control)
{
ContainerControl container = control as ContainerControl;
while (container != null)
{
control = container.ActiveControl;
container = control as ContainerControl;
}
return control;
}
答案 4 :(得分:2)
如果您以递归方式关注ActiveControl,它不会将您带到具有焦点的叶控件?
答案 5 :(得分:0)
ActiveControl并不总是可行,就像SplitContainer一样,ActiveControl.Focused为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;
}