Finden aller Controls mit gemeinsamer Basisklasse in einer Form mit TabControls

    • C#

      Finden aller Controls mit gemeinsamer Basisklasse in einer Form mit TabControls

      Zum Finden oder Ansprechen aller Label in einer Form genügt ja eine Schleife über die Controls-Property des Parents:

      C#-Quellcode

      1. foreach (Label lbl in this.Controls.OfType<Label>())
      2. {
      3. lbl.Text = "bla";
      4. }

      Befindet sich auf der Form ein TabControl mit mehreren Tabs, auf denen ebenfalls alle Labels erwischt werden sollen, müssen wir mit Rekursion arbeiten.
      Ich hab da mal auf Stackoverflow eine Vorlage gefunden, kann aber leider die Quelle nicht mehr finden:
      Auflisten aller Labels auf der Form

      C#-Quellcode

      1. /// <summary>
      2. /// Auflisten aller Labels auf der Form,
      3. /// auch aus Child-Controls
      4. /// </summary>
      5. private void button1_Click(object sender, EventArgs e)
      6. {
      7. IEnumerable<Control> c = GetAllType(this, typeof(Label));
      8. // was damit tun:
      9. //MessageBox.Show("Total Controls: " + c.Count());
      10. foreach (Label lbl in c)
      11. {
      12. lbl.Text = "bla";
      13. }
      14. }
      15. /// <summary>
      16. /// alle Objekte aufsammeln, deren aktuelle Instanz vom Type type ist
      17. /// </summary>
      18. /// <param name="control">das Parent-Control</param>
      19. /// <param name="type">der Ziel-Type</param>
      20. /// <returns>Auflistung der Controls</returns>
      21. private static IEnumerable<Control> GetAllType(Control control, Type type)
      22. {
      23. // alle Controls aufsammeln
      24. IEnumerable<Control> controls = control.Controls.Cast<Control>();
      25. // aus dieser nur die Ziel-Controls zurückgeben
      26. return controls.SelectMany(ctrl => GetAllType(ctrl, type))
      27. .Concat(controls)
      28. .Where(c => c.GetType() == type);
      29. }

      Nun wollen wir gezielt unsere meigenen UserControls ansprechen.
      Das ist im wesentlichen derselbe Code, nur dass wir in der static Prozedur GetType() durch eine Extension ersetzen müssen:
      Aufsammeln aller Objekte, die vom Type type abgeleitet sind

      C#-Quellcode

      1. /// <summary>
      2. /// Auflisten aller von UserControl abgeleiteten Controls auf der Form
      3. /// </summary>
      4. private void button2_Click(object sender, EventArgs e)
      5. {
      6. IEnumerable<Control> c = GetAllClasses(this, typeof(UserControl));
      7. // was damit tun:
      8. MessageBox.Show("Total UserControls: " + c.Count());
      9. foreach (UserControl ctrl in c)
      10. {
      11. UserControl1 ctrl1 = ctrl as UserControl1;
      12. UserControl2 ctrl2 = ctrl as UserControl2;
      13. if (ctrl1 != null) { ctrl1.SetText(); }
      14. if (ctrl2 != null) { ctrl2.SetText(); }
      15. }
      16. }
      17. /// <summary>
      18. /// alle Objekte aufsammeln, die vom Type type abgeleitet sind
      19. /// </summary>
      20. /// <param name="control">das Parent-Control</param>
      21. /// <param name="cls">der Ziel-Type</param>
      22. /// <returns>Auflistung der Controls</returns>
      23. private static IEnumerable<Control> GetAllClasses(Control control, Type cls)
      24. {
      25. // alle Controls aufsammeln
      26. IEnumerable<Control> controls = control.Controls.Cast<Control>();
      27. // aus dieser nur die Ziel-Controls zurückgeben
      28. return controls.SelectMany(ctrl => GetAllClasses(ctrl, cls))
      29. .Concat(controls)
      30. .Where(c => c.TestClass(cls));
      31. }

      Als Letztes sammeln wir alle Objekte auf, die ein gemeinsames Interface implementieren.
      Der Code ist wie der letzte, nur verwenden wir eine auf Interfaces zugeschnittene Extension:
      Aufsammeln aller Objekte, die das Interface interf implementieren

      C#-Quellcode

      1. /// <summary>
      2. /// Auflisten aller das Interface Interface1 implementierenden Controls auf der Form
      3. /// </summary>
      4. private void button3_Click(object sender, EventArgs e)
      5. {
      6. IEnumerable<Control> c = GetAllInterfaces(this, typeof(Interface1));
      7. // was damit tun:
      8. MessageBox.Show("Total Interfaces: " + c.Count());
      9. foreach (Interface1 ctrl in c) // VB2019 macht dies automatisch
      10. {
      11. if (ctrl != null) { ctrl.SetTextEx(); }
      12. }
      13. }
      14. /// <summary>
      15. /// alle Objekte aufsammeln, die das Interface interf implementieren
      16. /// </summary>
      17. /// <param name="control">das Parent-Control</param>
      18. /// <param name="interf">das Ziel-Interface</param>
      19. private static IEnumerable<Control> GetAllInterfaces(Control control, Type interf)
      20. {
      21. // alle Controls aufsammeln
      22. IEnumerable<Control> controls = control.Controls.Cast<Control>();
      23. // aus dieser nur die Ziel-Controls zurückgeben
      24. return controls.SelectMany(ctrl => GetAllInterfaces(ctrl, interf))
      25. .Concat(controls)
      26. .Where(c => c.TestInterface(interf));
      27. }

      Als Abschluss noch die Extensions:
      Extensions

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. namespace WindowsFormsApp2
      5. {
      6. /// <summary>
      7. /// Klasse mit Extension-Methoden
      8. /// </summary>
      9. static class MyExtensions
      10. {
      11. /// <summary>
      12. /// Überladung zum Testen, ob this Object
      13. /// von der Klasse cls abgeleitet ist
      14. /// </summary>
      15. /// <param name="ob">das Object</param>
      16. /// <param name="cls">die Klasse</param>
      17. /// <returns></returns>
      18. public static bool TestClass(this object ob, Type cls)
      19. {
      20. // Basisklassen abfragen
      21. return ob.GetType().IsSubclassOf(cls);
      22. }
      23. /// <summary>
      24. /// Überladung zum Testen, ob this Object
      25. /// das Interface interf implementiert
      26. /// </summary>
      27. /// <param name="ob">das Object</param>
      28. /// <param name="interf">das Interface</param>
      29. /// <returns></returns>
      30. public static bool TestInterface(this object ob, Type interf)
      31. {
      32. // Interfaces abfragen
      33. Type obType = ob.GetType();
      34. List<Type> inter = obType.GetInterfaces().ToList();
      35. return inter.Contains(interf);
      36. }
      37. }
      38. }

      Beim Testen hat mich das Studio 2019 überrascht, dass dort eine Reihe von Casts automatisiert werden.
      Im Teil mit den Interfaces habe ich das übernommen, bei den Basisklassen nicht, dort kam dieser Vorschlag:

      Und für alle, die den Code nicht kopieren wollen, die komplette Solution:
      WindowsFormsApp2.zip
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!