Каков предпочтительный способ найти сфокусированный контроль в приложении WinForms? - PullRequest
62 голосов
/ 12 января 2009

Каков предпочтительный / самый простой способ найти элемент управления, который в данный момент получает пользовательский ввод (ввод с клавиатуры) в 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, похоже, возвращает только непосредственный активный дочерний элемент в дереве элементов управления, пока я ищу листовой элемент управления).

Ответы [ 6 ]

64 голосов
/ 13 января 2009

Если у вас уже есть другие вызовы 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;
}
25 голосов
/ 12 января 2009

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

.NET Framework библиотеки не предоставляет вам 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;
          }
} 
20 голосов
/ 20 декабря 2009

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

В вашем примере, если TextBox имеет Focus: then: for Form, TableLayoutPanel и FlowLayoutPanel: свойство ActiveControl для всех из них будет TextBox!

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

Другие элементы управления, которые по своей конструкции содержат другие элементы управления, такие как TableLayOutPanel, GroupBox, Panel, FlowLayoutPanel и т. Д., Являются не типом ContainerControl, и они не предоставляют KeyEvents.

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

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

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

И, да, измененный код ответа будет работать, если вы передадите ему «подлинный» ControlContainer, например экземпляр Form, который находится в пути родительского наследования ActiveControl, но вы все еще просто тратите время, дублируя функцию «ActiveControl.

Итак, что такое «подлинные» Контейнерные Контроли: посмотрите их: Документы MS для Контейнерного Контроля

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

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

Теперь, если реальное намерение вашего вопроса было спросить, как найти самый внешний Контейнерный Контроллер ... , которого нет в самой Форме .. обычного неконтейнера Элемент управления вложил несколько произвольных уровней в глубину ... Вы можете использовать некоторые из идей в ответе, но код может быть значительно упрощен.

Регулярные элементы управления, ContainerControls, UserControls и т. Д. (Но не Form!) Имеют свойство «Container», к которому вы можете получить доступ, чтобы получить их непосредственный контейнер, но убедитесь, что у вас есть «final Container» в их пути наследования, который не является Form требуется некоторый код для «обхода» дерева наследования, что продемонстрировано здесь.

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

Надеюсь, это поможет.

8 голосов
/ 11 апреля 2009

Решение 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;
    }
2 голосов
/ 13 января 2009

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

0 голосов
/ 15 мая 2019

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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...