Подтвердить что ты не робот

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

4b9b3361

Ответ 1

Если у вас есть другие вызовы в 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;
}

Ответ 2

После поиска в Интернете я нашел следующее в Часто задаваемые вопросы о Windows Forms от George Shepherd

. Библиотеки framework.Net не предоставляют вам API для запроса сфокусированное управление. Вы должны вызовите API окон для этого:

[С#]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 4

Решение 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;
    }

Ответ 5

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

Ответ 6

ActiveForm не всегда работает, как с SplitContainer.

Так что для более надежного метода можно сделать что-то вроде этого:

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