找到当前在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似乎只是在控制树中返回直接活动的子节点,而我正在寻找叶子控件).
如果您已经对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; }
在搜索互联网之后,我在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; } }
Form或Container上的ActiveControl 将返回该实体的主动控件,无论它嵌套在其他容器中的深度.
在您的示例中,如果TextBox具有Focus:则:对于Form,TableLayoutPanel和FlowLayoutPanel:所有这些的ActiveControl属性将是TextBox!
一些(但不是全部)"真正的"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:检查它们:ContainerControl的MS文档
只有彼得的回答真的回答了明确的问题,但是这个答案带有使用互操作的代价,而'ActiveControl会给你你需要的东西.
另请注意,每个Control(容器或非容器)都有一个永远不为null的Controls Collection,而且很多(我从来没有尝试过所有这些:我为什么会这样?)基本的WinForms控件让你做到"疯狂" "喜欢将控件添加到像Button这样的'简单'控件的ControlCollection而没有错误.
现在,如果您的问题的真正意图是询问您如何找到最外层的ContainerControl ... 那不在Form本身上 ......一个常规的非容器控件嵌套了一些任意级别的深层...你可以使用一些答案中的想法:但代码可以大大简化.
常规控件,ContainerControls,UserControls等(但不是Form!)都有一个'容器属性,你可以访问以获取他们的直接容器,但确保你的inhertance路径中的'最终容器不是一个Form需要一些代码"继续"继承树,这是在这里演示的.
您可能还希望查看'Control的'HasChildren属性,它通常用于处理WinForms中的Focus,ActiveControl和Select问题.回顾Select和Focus之间的区别在这里很有价值,并且SO有一些很好的资源.
希望这可以帮助.
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; }