代码之家  ›  专栏  ›  技术社区  ›  Milan Gardian

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

  •  61
  • Milan Gardian  · 技术社区  · 16 年前

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

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

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

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

    var focused = FindFocusedControl(this);
    

    这是否恰当?

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

    Form
        TableLayoutPanel
            FlowLayoutPanel
                TextBox (focused)
    

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

    5 回复  |  直到 16 年前
        1
  •  75
  •   Hinek    10 年前

    我将采用非递归方法:

    public static Control FindFocusedControl(Control control)
    {
        var container = control as IContainerControl;
        while (container != null)
        {
            control = container.ActiveControl;
            container = control as IContainerControl;
        }
        return control;
    }
    
        2
  •  27
  •   Xn0vv3r    16 年前

    在网上搜索之后,我在 George Shepherd's Windows Forms FAQ

    调用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;
              }
    } 
    
        3
  •  21
  •   BillW    15 年前

    窗体或容器上的ActiveControl

    在您的示例中,如果文本框具有Focus:then:for表单、TableLayoutPanel和FlowLayoutPanel:the'ActiveControl属性 在他们当中

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

    根据设计,包含其他控件(如TableLayOutPanel、GroupBox、Panel、FlowLayoutPanel等)的其他控件包括 键入ContainerControl,它们不会公开KeyEvents。

    有没有试过投球 实例 直接地 to ContainerControl不会编译:它们不是ContainerControl类型。

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

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

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

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

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

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

    现在如果 真实意图 你的问题之一是问你如何找到最外层的集装箱控制器 ... 这不在表格上 控件嵌套了一些任意深度的级别。。。你可以使用一些 思想 答案是:但是代码可以大大简化。

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

    希望这有帮助。

        4
  •  9
  •   Nate Cook    15 年前

    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;
        }
    
        5
  •  2
  •   sliderhouserules    16 年前

        6
  •  0
  •   colin lamarre    5 年前

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