2013-03-03 17 views
9

chciałbym dodać zdarzenie do wszystkich pól tekstowych na moim formularzu:pętli wszystkich formantów na formularzu, nawet te w groupboxes

foreach (Control C in this.Controls) 
{ 
    if (C.GetType() == typeof(System.Windows.Forms.TextBox)) 
    { 
     C.TextChanged += new EventHandler(C_TextChanged); 
    } 
} 

Problemem jest to, że są one przechowywane w kilku groupboxes i moim pętli ich nie widzi. Mógłbym przeglądać kontrolki poszczególnych skrzynek grupowych indywidualnie, ale czy można to zrobić w prosty sposób w jednej pętli?

+0

Możesz to zrobić w pętli rekurencyjnej. – jac

Odpowiedz

0

Jak już wspomniano, będziesz musiał przejść głębiej niż po prostu jazda rowerem po każdym elemencie w formularzu. To, niestety, implikuje użycie zagnieżdżonej pętli.

W pierwszej pętli przejdź przez każdy element. JEŻELI element jest typu GroupBox, to wiesz, że musisz przejść przez każdy element wewnątrz skrzynki grupowej, zanim przejdziesz dalej; jeszcze dodaj wydarzenie jak zwykle.

Wydaje się, że masz przyzwoity chwyt C#, więc nie dam ci żadnego kodu; wyłącznie w celu zapewnienia, że ​​opracujesz wszystkie ważne koncepcje, które są zaangażowane w rozwiązywanie problemów :)

18

Zbiór formularzy i kontenerów zawiera tylko najbliższe dzieci Controls. Aby uzyskać wszystkie kontrole, trzeba przemierzać drzewa kontroli i zastosować tę operację rekurencyjnie

private void AddTextChangedHandler(Control parent) 
{ 
    foreach (Control c in parent.Controls) 
    { 
     if (c.GetType() == typeof(TextBox)) { 
      c.TextChanged += new EventHandler(C_TextChanged); 
     } else { 
      AddTextChangedHandler(c); 
     } 
    } 
} 

Uwaga: W wywodzi postaci (pośrednio) z Control jak również i wszystkie kontrole mają Controls kolekcję. Więc można wywołać metodę takiego w swojej formie:

AddTextChangedHandler(this); 

Bardziej ogólnie rozwiązaniem byłoby tworzenie metodę rozszerzenia, które odnoszą się działania rekurencyjnie do wszystkich kontroli. W klasie statyczne (np WinFormsExtensions) dodać metody:

public static void ForAllControls(this Control parent, Action<Control> action) 
{ 
    foreach (Control c in parent.Controls) { 
     action(c); 
     ForAllControls(c, action); 
    } 
} 

namespace klasy statyczne muszą być „widoczny”, to znaczy, dodanie odpowiedniego zgłoszenia using jeżeli w innym nazw.

Następnie możesz to tak nazwać, gdzie jest this; można również wymienić this przez zmienną formularza lub kontrolnej, której zagnieżdżone kontrole mają zostać dotknięte:

this.ForAllControls(c => 
{ 
    if (c.GetType() == typeof(TextBox)) { 
     c.TextChanged += C_TextChanged; 
    } 
}); 
8

Kilka prostych, ogólnych narzędzi zastosowania uczynić ten problem bardzo proste. Możemy stworzyć prostą metodę, która przemierzy całe drzewo kontrolne, zwracając sekwencję wszystkich jego dzieci, wszystkich ich dzieci itd., Obejmując wszystkie kontrolki, a nie tylko ustaloną głębię. Możemy użyć rekursji, ale unikanie rekursji będzie działać lepiej.

public static IEnumerable<Control> GetAllChildren(this Control root) 
{ 
    var stack = new Stack<Control>(); 
    stack.Push(root); 

    while (stack.Any()) 
    { 
     var next = stack.Pop(); 
     foreach (Control child in next.Controls) 
      stack.Push(child); 
     yield return next; 
    } 
} 

Używanie tego możemy dostać wszystkie dzieci, odfiltrować te typu musimy, a następnie dołączyć obsługi bardzo prościej:

foreach(var textbox in GetAllChildren().OfType<Textbox>()) 
    textbox.TextChanged += C_TextChanged; 
1

Nie widziałem nikogo przy użyciu LINQ i/lub wydajność tak tu idzie:

public static class UtilitiesX { 

    public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl) 
    { 
     yield return rootControl; 
     foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree())) 
     { 
      yield return childControl; 
     } 
    } 

    public static void ForEach<T>(this IEnumerable<T> en, Action<T> action) 
    { 
     foreach (var obj in en) action(obj); 
    } 
} 

następnie można użyć go do pragnienia twojego serca:

someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler); 
5

Spróbuj

AllSubControls(this).OfType<TextBox>().ToList() 
    .ForEach(o => o.TextChanged += C_TextChanged); 

gdzie AllSubControls jest

private static IEnumerable<Control> AllSubControls(Control control) 
    => Enumerable.Repeat(control, 1) 
     .Union(control.Controls.OfType<Control>() 
           .SelectMany(AllSubControls) 
      ); 

LINQ jest super!

0

można pętla tylko przez otwarte formy w Windows Forms z wykorzystaniem kolekcji formularz na przykład, aby ustawić okna pozycji wyjściowej dla wszystkich otwartych formach:

public static void setStartPosition() 
     { 
      FormCollection fc = Application.OpenForms; 

      foreach(Form f in fc) 
      { 
       f.StartPosition = FormStartPosition.CenterScreen; 
      } 
     } 
0

wiem, że jest to starszy wątek, ale powiedziałbym, kod fragment z http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/ to sprytne rozwiązanie tego problemu.

Używa metody rozszerzenia dla ControlCollection.

public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action) 
{ 
    foreach (Control control in controlCollection) 
    { 
     if (!string.IsNullOrEmpty(tagFilter)) 
     { 
      if (control.Tag == null) 
      { 
       control.Tag = ""; 
      } 

      if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T) 
      { 
       action(control); 
      } 
     } 
     else 
     { 
      if (control is T) 
      { 
       action(control); 
      } 
     } 

     if (control.Controls != null && control.Controls.Count > 0) 
     { 
      ApplyToAll(control.Controls, tagFilter, action); 
     } 
    } 
} 

Teraz, aby przypisać zdarzenie do wszystkich kontrolek TextBox można napisać oświadczenie jak (gdzie „to” jest formą):

this.Controls.ApplyToAll<TextBox>("", control => 
{ 
    control.TextChanged += SomeEvent 
}); 

Opcjonalnie można filtrować elementów sterujących przez ich znaczniki .