Как программно получить все элементы из формы?

287
21 сентября 2021, 05:30

Передо мной встала задача получить все элементы в форме, включая меню и строку состояния и другие у которых имеется свойство Text. Так как у каждого контрола имеется свойство Controls (и у формы соответственно тоже), то я могу использовать нижеприведённый код для получения всех контролов формы и их потомков:

public static IEnumerable<Control> GetControls(Control control)
{
    foreach (Control childControl in control.Controls)
    {
        foreach (Control grandChild in GetControls(childControl))
        {
            yield return grandChild;
        }
        yield return childControl;
    }
}

Проблема заключается в том, что у меню не имеется свойства Controls и для доступа к дочерним элементам необходимо использовать свойство Items. На данный момент не нашёл ничего другого, как проверять тип. Можно ли каким либо образом единообразно получать доступ ко всем элементам формы включая дочерние?

Answer 1

Свойство Items содержит элементы типа ToolStripItem, а не Control. Они не унаследованы друг от друга, поэтому невозможно добавить тулстрипайтем в последовательность контролов.

Общим непосредственным предком этих классов является Component, у которого нет свойства Text. Ни один из интерфейсов, которые они реализуют, тоже не имеет такого свойства. Поэтому в лоб ваша задача не решается.

Вы пишете, что не нашли ничего другого, как проверять тип. Да, тип придётся проверять в любом случае. Можно делать это внутри вашего метода и прямо там же выполнять необходимые действия.

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

public static IEnumerable<Component> GetComponents(Control control)
{
    foreach (Control childControl in control.Controls)
    {
        if (childControl is ToolStrip toolStrip)
        {
            foreach (ToolStripItem item in toolStrip.Items)
                yield return item;
        }
        else
        {
            foreach (Component component in GetComponents(childControl))
                yield return component;
        }
        yield return childControl;
    }
}

Здесь мы получаем последовательность типа Component, хотя с таким же успехом это могут быть object - в любом случае далее придётся проверять их тип.

Можно избежать ручной проверки типов, используя dynamic:

foreach (var component in GetComponents(this))
{
    ((dynamic)component).Text = "something";
}

Это более-менее рабочий код, но остаётся много вопросов. Например, ToolStrip может содержать ToolStripDropDownButton, который не имеет свойства Items, но имеет DropDownItems. Сильно подозреваю, что по ним вы тоже хотели бы пройтись.

Всё это наводит на мысли, что вы не с той стороны подходите к решению некоей своей проблемы.

А вообще, я бы сделал два (или больше, с учётом DropDownов) метода, один из которых возвращал бы Control, другой ToolStripItem, третий - ToolStripDropDownItem.

Answer 2

При добавлении элемента MenuStrip средствами конструктора форм, IDE добавляет private поле соответствующего типа в ваш класс формы (можно посмотреть в FormName.Designer.cs). Если вы хотите получить этот элемент из другой формы, можно попробовать использовать рефлексию:

FieldInfo menuStrip = myControl.GetType().GetField("menu_strip_field_name", 
            BindingFlags.NonPublic | BindingFlags.Instance);

или перебрать все поля Control'а, и забрать все с типом MenuStrip

List<MenuStrip> items = new List<MenuStrip>();
foreach(var item in myControl.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
   if(item.FieldType == typeof(MenuStrip))
   {
      items.Add((MenuStrip)item.GetValue(myControl));
   }
}
READ ALSO
Как убрать гласные буквы из массива

Как убрать гласные буквы из массива

Цель: из 10 элементов массива вывести в консоль только согласные буквы английского алфавитаТ

209
социальная сеть на asp.NET Core + Angular 8 [закрыт]

социальная сеть на asp.NET Core + Angular 8 [закрыт]

Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме

158
C#.Почему массивы-типы ссылок?

C#.Почему массивы-типы ссылок?

Узнал что массивы в C#-это типы ссылок и они хранятся в куче,которая используется для динамичного выделения памятиНо вот вопрос:для чего массиву...

166
Получить текст с экрана

Получить текст с экрана

Я переписываю код WPF в UWPЗадача состоит в том, чтобы сделать скриншот экрана, вырезать его и скормить его библиотеке, которая найдет текст...

77