Что это предпочтительный способ, чтобы найти элемент управления с фокусом ввода в приложение приложения 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)

(formInstance).ActiveControl вернет ссылку на TableLayoutPanel, а не на текстовое поле (потому что ActiveControl, похоже, возвращает только непосредственный активный дочерний элемент в дереве элементов управления, пока я ищу элемент управления leaf).

5 59

5 ответов:

Если у вас уже есть другие вызовы API Windows, нет никакого вреда в использовании решения Peters. Но я понимаю ваши опасения по этому поводу и склоняюсь к аналогичному решению, как у вас, используя только функциональные возможности фреймворка. В конце концов, разница в производительности (если она есть) не должна быть значительной.

Я бы взял нерекурсивный подход:

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

после поиска в Интернете, я нашел следующее на Windows Forms Джорджа Шепарда FAQ

библиотеки .Net framework не предоставляют API для запроса сфокусированный контроль. Ты должен ... вызвать API-интерфейса Windows, чтобы сделать это:

[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 на форме или контейнере будет возвращает активный элемент управления этой сущности независимо от того, насколько глубоко он может быть вложен в другие контейнеры.

в вашем примере, если текстовое поле имеет фокус: затем : для формы, TableLayoutPanel и FlowLayoutPanel: свойство ActiveControl все они будет поле !

некоторые, но не все, "подлинные" типы ContainerControl ... как форма и UserControl ... ключевые разоблачить События (в случае формы: только если форма.KeyPreview = = true можно ли их использовать) .

другие элементы управления, которые, по замыслу, содержать другие элементы управления, такие как элемент управления tablelayoutpanel, группа, Панель, управления flowlayoutpanel и т. д. являются не введите ContainerControl, и они не предоставляют KeyEvents.

любая попытка бросить экземпляров объектов, таких как TextBox, FlowLayoutPanel, TableLayoutPanel напрямую to ContainerControl не будет компилироваться : их нет ContainerControl типа.

код в принятом ответе и в следующем ответе, который исправляет орфографические ошибки первого ответа, будет компилировать / принимать экземпляры вышеперечисленных параметров, потому что вы "понижаете" их до типа "Control, делая тип параметра" Control

но в каждом случае приведение к ControlContainer будет возвращать null, а переданный экземпляр будет возвращен (downcasted): по существу no-op.

и, да, измененный код ответа будет работать, если вы передадите ему "подлинный" ControlContainer, например экземпляр формы, который находится в Родительском пути наследования ActiveControl, но вы все еще просто тратите время на дублирование функции 'ActiveControl.

Итак, что такое "подлинные" ContainerControls: проверьте их:MS docs для ContainerControl

только ответ Питера действительно отвечает на явный вопрос, но этот ответ несет цену использования взаимодействия, и - Активный контроль даст вам то, что вам нужно.

также обратите внимание, что каждый элемент управления (контейнер или не контейнер) имеет коллекцию элементов управления, которая никогда не является нулевой, и что много (я никогда не пробовал все из них : зачем мне ?) основной элемент управления WinForms позволяет вам делать "сумасшедшие вещи", такие как добавление элементов управления в коллекцию ControlCollection "простых" элементов управления, таких как кнопка без ошибки.

теперь, если реальные намерения из вашего вопроса было спросить, как вы находите самый внешний ContainerControl ... это не на самой форме ... а обычные номера-контейнер управление вложенные некоторые произвольные уровни глубоко ... вы можете использовать некоторые из идеи в ответ: но код может быть значительно упрощен.

регулярный контроль, ContainerControls, элементы управления UserControl и т. д. (но не форма !) у всех есть свойство "контейнер", к которому вы можете получить доступ, чтобы получить свой непосредственный контейнер, но убедитесь, что у вас есть " окончательный контейнер в их путь наследования, который не является формой, требует некоторого кода для "обхода" дерева наследования, которое показано здесь.

вы также можете проверить свойство HasChildren элемента управления, который обычно полезен при решении вопросов Focus, ActiveControl и Select в WinForms. Обзор разницы между Select и Focus может быть ценным здесь, и поэтому имеет некоторые хорошие ресурсы по этому вопросу.

надеюсь, что это помогает.

решение Хинека хорошо работает для меня, за исключением того, что это 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;
    }

Если вы следуете ActiveControl из рекурсивно это не приведет вас к листу управления, который имеет фокус?