2015-09-28 14 views
2

У меня есть несколько контейнеров на моей WinForm, например Panel, SpliContainer, StatusStrip ... Каждый из этих контейнеров содержит основные элементы, такие как кнопки или текстовые поля. Мне нужно перебирать все элементы управления формы (даже те, что находятся в Панелях, SplitContainers, StatusStrip), чтобы найти некоторый элемент управления. Я пытаюсь с рекурсивной функциейКак получить список всех элементов управления WinForm, даже тех, что находятся в SplitContainers или панелях

ListAllControls(Control MainControl) 
    { 
     foreach (Control control in MainControl.Controls) 
     { 
      if (control.HasChildren) 
      { 
       ListAllControls(control); 
      } 
      else 
      { 
       // do something with this control 
      } 
     } 
    } 

но я не получаю элементы управления, которые находятся в контейнерах !?

UPDATE:

У меня есть форма с SplitContainer, панели и StatuStrip. В каждом из этих элементов управления у меня есть несколько дочерних элементов управления, таких как toolStripStatusLabel1 в StatuStrip1. Проблема в том, что я пытаюсь найти, например, control toolStripStatusLabel1 в StatuStrip через функцию ListAllControls. Я не могу ее найти !? Я не знаю другого подхода, чтобы получить все элементы управления из формы. Полный код здесь:

class Control_Finder 
{ 

    private Control founded_control = null; 

    public Control Founded_Control 
    { 
     get { return founded_control; } 
    } 

    public void ListAllControls(Control MainControl, string SearchForControl) 
    { 
     foreach (Control control in MainControl.Controls) 
     { 
      if (control.HasChildren) 
      { 
       ListAllControls(control, SearchForControl); 
      } 
      else 
      { 
       // check if control has searched name 
       if (control.Name == SearchForControl) 
       { 
        founded_control = control; 
        return; 
       } 
      } 
     } 
    } 
} // class 

Пример: «Я нашел контроль StatusStrip1»

Form Me = this; 
Control_Finder Test = new Control_Finder(); 
Test.ListAllControls(Me, "toolStripStatusLabel1"); 

if (Test.Founded_Control != null) 
{ 
     MessageBox.Show("I found control " + Test.Founded_Control.Name + "!"); 
} 
else 
{ 
     MessageBox.Show("Didn't found! :("); 
} 

В этом примере я получаю Не нашел :( но если я использую StatusStrip1 я получаю Надеюсь, что вопрос стал более ясным, чем раньше.

+0

Что произошло, когда вы активизировали через код в отладчике? Получил ли код какой-либо из рассматриваемых контейнеров? Если да, то что произошло, когда оператор 'foreach' попытался перечислить дочерний элемент контейнера? Код, который вы опубликовали, прост и должен работать нормально. Таким образом, ваша проблема заключается в коде, который вы не опубликовали. Предоставьте [хороший, _minimal_, _complete_ пример кода] (https://stackoverflow.com/help/mcve), который надежно воспроизводит проблему. –

+0

Это все код. Это простая рекурсивная функция, которая должна найти элемент управления по его имени. Отладчик корректно выполняет итерацию всех форм-корневых элементов управления, которые не являются контейнерами управления. Я вижу их имена и атрибуты без проблем. Когда шаг в некоторые контейнеры обычно отображает пустую строку для имени и атрибутов. Просто упомянуть, что я пробовал с

.Controls.Find («<имя_контроля>», true) и такая же проблема возникает! – Milos

+0

_ «Это все код» _ - если вы имеете в виду «это все ** код **», то это явно неверно. Пожалуйста, прочитайте страницу по ссылке, которую я предоставил, чтобы увидеть, что подразумевается под ** минимальным ** и ** полным **, а также для объяснения причин такого примера кода. –

ответ

3

Было бы лучше, если бы вы предоставили a good, minimal, complete code example, что четко показывает ваши конкретные cenario. Однако, основываясь на информации, добавленной вами к вашему вопросу, я смог создать то, что, по моему мнению, является примером репрезентативного кода. Этот ответ основан на этом примере.


В качестве комментатора LarsTech отмечает, что ToolStripStatusLabel не наследует Control. Наиболее конкретным базовым классом, разделяемым Control и ToolStripStatusLabel, является Component. Поэтому, по крайней мере, у вас есть большая проблема с вашей целью вернуть объект типа Control и все же найти экземпляр ToolStripStatusLabel. Даже если вы нашли объект, вы не сможете его отличить до Control.

Другая проблема заключается в том, что в то время как ToolStrip сам делает наследуют Control класс, он не хранит своих детей в собственность Controls (т.е. в ControlCollection объекта). Я предполагаю, что это потому, что его дети не являются объектами Control и поэтому не могут быть сохранены в ControlCollection. В любом случае это означает, что по мере того, как вы рекурсивно просматриваете графику объекта вашей формы, вы должны обрабатывать ToolStrip иначе, чем другие экземпляры Control, чтобы найти своих детей.


Вот пример программа, которая демонстрирует один подход, который будет работать (см в нижней части этого поста для дизайнера сгенерированного кода, который идет с этим примером):

Form1.CS:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     Component component = FindControl(this.Controls, "toolStripStatusLabel1"); 

     label2.Text = component != null ? 
      "Found control named \"" + GetNameForComponent(component) + "\"" : 
      "No control was found"; 
    } 

    private static string GetNameForComponent(Component component) 
    { 
     Control control = component as Control; 

     if (control != null) 
     { 
      return control.Name; 
     } 

     ToolStripItem item = component as ToolStripStatusLabel; 

     if (item != null) 
     { 
      return item.Name; 
     } 

     return "<unknown Component type>"; 
    } 

    private Component FindControl(IEnumerable controlCollection, string name) 
    { 
     foreach (Component component in controlCollection) 
     { 
      if (GetNameForComponent(component) == name) 
      { 
       return component; 
      } 

      IEnumerable childControlCollection = GetChildrenForComponent(component); 

      if (childControlCollection != null) 
      { 
       Component result = FindControl(childControlCollection, name); 

       if (result != null) 
       { 
        return result; 
       } 
      } 
     } 

     return null; 
    } 

    private static IEnumerable GetChildrenForComponent(Component component) 
    { 
     ToolStrip toolStrip = component as ToolStrip; 

     if (toolStrip != null) 
     { 
      return toolStrip.Items; 
     } 

     Control control = component as Control; 

     if (control != null) 
     { 
      return control.HasChildren ? control.Controls : null; 
     } 

     return null; 
    } 
} 

Из несвязного характера наследования объекта для объектов, которые вы имеете дело с, некоторыми специальными обработки должны быть сделаны:

  1. товаров вы найдете могут быть случаями Control или нет , Экземпляры Control хранят свое имя в свойстве Control.Name, но, очевидно, экземпляры, которые не являются Control объектов, не будут. Их нужно обрабатывать, в частности, путем идентификации типа, в котором хранится их свойство имени (в этом случае ToolStripItem.Name, но обратите внимание, что существуют другие типы не Control, которые наследуют Component, и каждый из них должен обрабатываться отдельно). Я добавил метод GetNameForComponent(Component), чтобы инкапсулировать это.
  2. деталей вы найдете которые являются экземплярами Control, некоторые из них будут хранить их детей в Controls коллекции, в то время как другие не будут, а не с помощью какой-либо другой собственности для ссылки на коллекцию, в которой хранятся дети. Опять же, вспомогательный метод (в данном случае, GetChildrenForComponent(Component)) предоставляется для инкапсуляции этой разницы.

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

В моем примере я реализовал его просто как один метод, который возвращает объект, о котором идет речь, если он найден.

Также обратите внимание, что ваша реализация не найдет объект, который сам является контейнером объектов. Я исправил это и в своих примерах.


Последнее примечание: следуя вышеуказанной стратегии, вам нужно будет добавить код для каждого интересующего базового класса. Возможно, вышеуказанное достаточно для ваших нужд, или, возможно, у вас есть другие типы контейнеров, которые содержат объекты не Control и которые не наследуют ToolStrip. Если это так, вам нужно будет добавить дополнительные случаи в каждом из вспомогательных методов, соответствующих этим типам.

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

Но даже в этом простом примере использование отражения для получения детей довольно сложно. Вместо того, чтобы дети всегда находились в объекте коллекции, полученном из свойства, названного, например, Controls, в одном случае это имя, но в другом случае это имя Items. Можно копать глубже, например, определить тип объектов, найденных в коллекции, но код начинает усложняться в этой точке.

Учитывая, что это спорный вопрос, действительно ли это даже имеет смысл лечить ToolStrip детей так же, как регулярные Control детей в форме, во всяком случае, я бы не советовал вкладывать много усилий в действительно обобщенном решении. Лучше обращаться с отдельными случаями по мере необходимости, чтобы напомнить себе, что то, что вы делаете, на самом деле не такая хорошая идея.:)


Form1.Designer.cs:

partial class Form1 
{ 
    /// <summary> 
    /// Required designer variable. 
    /// </summary> 
    private System.ComponentModel.IContainer components = null; 

    /// <summary> 
    /// Clean up any resources being used. 
    /// </summary> 
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 
    protected override void Dispose(bool disposing) 
    { 
     if (disposing && (components != null)) 
     { 
      components.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 

    #region Windows Form Designer generated code 

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor. 
    /// </summary> 
    private void InitializeComponent() 
    { 
     this.statusStrip1 = new System.Windows.Forms.StatusStrip(); 
     this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); 
     this.button1 = new System.Windows.Forms.Button(); 
     this.label1 = new System.Windows.Forms.Label(); 
     this.label2 = new System.Windows.Forms.Label(); 
     this.statusStrip1.SuspendLayout(); 
     this.SuspendLayout(); 
     // 
     // statusStrip1 
     // 
     this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); 
     this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 
     this.toolStripStatusLabel1}); 
     this.statusStrip1.Location = new System.Drawing.Point(0, 228); 
     this.statusStrip1.Name = "statusStrip1"; 
     this.statusStrip1.Size = new System.Drawing.Size(282, 25); 
     this.statusStrip1.TabIndex = 0; 
     this.statusStrip1.Text = "statusStrip1"; 
     // 
     // toolStripStatusLabel1 
     // 
     this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; 
     this.toolStripStatusLabel1.Size = new System.Drawing.Size(111, 20); 
     this.toolStripStatusLabel1.Text = "Strip status text"; 
     // 
     // button1 
     // 
     this.button1.Location = new System.Drawing.Point(12, 12); 
     this.button1.Name = "button1"; 
     this.button1.Size = new System.Drawing.Size(75, 23); 
     this.button1.TabIndex = 1; 
     this.button1.Text = "button1"; 
     this.button1.UseVisualStyleBackColor = true; 
     this.button1.Click += new System.EventHandler(this.button1_Click); 
     // 
     // label1 
     // 
     this.label1.AutoSize = true; 
     this.label1.Location = new System.Drawing.Point(12, 38); 
     this.label1.Name = "label1"; 
     this.label1.Size = new System.Drawing.Size(52, 17); 
     this.label1.TabIndex = 2; 
     this.label1.Text = "Result:"; 
     // 
     // label2 
     // 
     this.label2.AutoSize = true; 
     this.label2.Location = new System.Drawing.Point(70, 38); 
     this.label2.Name = "label2"; 
     this.label2.Size = new System.Drawing.Size(0, 17); 
     this.label2.TabIndex = 3; 
     // 
     // Form1 
     // 
     this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 
     this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 
     this.ClientSize = new System.Drawing.Size(282, 253); 
     this.Controls.Add(this.label2); 
     this.Controls.Add(this.label1); 
     this.Controls.Add(this.button1); 
     this.Controls.Add(this.statusStrip1); 
     this.Name = "Form1"; 
     this.Text = "Form1"; 
     this.statusStrip1.ResumeLayout(false); 
     this.statusStrip1.PerformLayout(); 
     this.ResumeLayout(false); 
     this.PerformLayout(); 

    } 

    #endregion 

    private System.Windows.Forms.StatusStrip statusStrip1; 
    private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; 
    private System.Windows.Forms.Button button1; 
    private System.Windows.Forms.Label label1; 
    private System.Windows.Forms.Label label2; 
} 
+0

Большое спасибо, Питер Дунихо. Моя проблема заключается в том, что я не понимал разницы между стандартными элементами управления и элементами управления StatusStrip. Ваша идея (решение), чтобы лечить ее по-другому, здорово! Еще раз спасибо и приветствую! – Milos

 Смежные вопросы

  • Нет связанных вопросов^_^