C# WinForms viele Comboboxen befüllen und auslesen

Halus

Lt. Junior Grade
Registriert
Juli 2013
Beiträge
373
Hi,
Ich bin neu bei C# und kenne mich vor allem bei UI nicht so aus.

Ich habe eine ganz simple UI mit einer Tabbed Pane.
Insgesamt sind es 5 Tabs und in jedem Tab befinden sich 10 Comboboxen.
Die Daten in den 10 Boxen sind Unterschiedlich, allerdings sind die Daten für die unterschiedlichen Tabs die selben. Also, die erste Combobox in einem Tab hat immer die selben Daten wie in allen anderen Tabs.

Ich habe entsprechend 10 Lists welche die Daten für die Boxen darstellen.
Nun möchte ich die Comboboxen befüllen und wieder auslesen wenn der User ein Item auswählt.
Wie mache ich das ohne, dass ich 50x die Boxen ansprechen muss?

Am besten wäre es wohl wenn ich die boxen per Code generiere und dann per Arrays darauf zugreife? Wie würde ich das machen und dann bei der Selection weiter?

Und nachdem der User die Daten ausgewählt hat muss ich eben diese speichern, wobei ich für einen Tab ein Objekt vorgesehen habe und darin die Variablen den Boxen entsprechen. Wieder die selbe Frage, wie die ganzen Daten speichern ohne hundert Mal Seter und Getter aufzurufen?
 
Wie du schon geschrieben hast, macht es am meisten sinn die Komboboxen (und auch gleich die Tab-Panels) im code zu erstellen.

Jedem GUI Element kannst du susätzlich immer ein Object mitgeben(heist Tag soweit Ich mich erinnere), irgendeines. In deinem fall wäre das ein Object wo drin steht welchen Zustand die Kombobox hat. Immer den Komboboxen die zusammen gehören gibtst du das geiche Object mit. Ausserdem gibst du deinem Object mit welchen Komboboxen du es angefügt hast (list<Combobox>)

Und jetzt der Trick 77: Verwende das Changed event. In diesem liest du aus dem sender (ist deine Kombobox) dein Object aus, änderst deren zustand und gehst über die liste der Comboboxen,
prüfst ob diese nicht identisch deiner aktuellen sind -> Entfernst das Change event -> Ändere den Wert auf den in deinem Object -> füge das Change event wieder an.

OOP ist dir ein begriff? In deinem anwendungsbeispiel ist OOP nämlich etwas vom wichtigsten.

Noch fragen? Ist nicht al zu ausführlich, aber Ich denke mal das kriegst du hin, und sonst fragst di hier einfach nach^^

EDIT: wenn du es natürlich ganz gut machen willst, kannst du deinem Object einen ChangeEvent mitgeben, welches eine Kombobox oder null übergeben bekommt und die Komboboxen dann entsprechend anpasst^^.

Gruss Multi
 
Zuletzt bearbeitet:
Ich wäre vorsichtig dabei Controls im Code zu erstellen, das wird sehr schnell unübersichtlich.

In WPF wäre das einfach, aber in Windows Forms gibt es denke ich keine einfache Möglichkeit das zu realisieren

Mein Ansatz wäre eine Klasse zu erstellen die immer eine Liste von Controls führt die zusammenhängen, dann gibt es noch Properties für die Liste der vorhandenen Items und das selektierte Item.

Bei Änderung weißt du die neuen Daten diesen beiden Properties zu und in der jeweiligen Set Methode machst du eine Schleife durch die Zugeordneten Controls und aktualisierst diese.

Die Klasse:

Code:
class ComboBoxGroup
    {
        public List<ComboBox> ComboBoxList { get; set; }
        public List<string> Items
        {
            set
            {
                foreach (ComboBox cbo in ComboBoxList)
                {
                    cbo.DataSource = value;
                }
            }
        }
        public int SelectedIndex
        {
            set
            {
                foreach(ComboBox cbo in ComboBoxList)
                {
                    cbo.SelectedIndex = value;
                }
            }
        }
        public ComboBoxGroup()
        {
            ComboBoxList = new List<ComboBox>();
        }
    }

Verwendung:

Code:
  public partial class Form1 : Form
    {

        ComboBoxGroup group1 = new ComboBoxGroup();
        ComboBoxGroup group2 = new ComboBoxGroup();

        public Form1()
        {
            InitializeComponent();
            //Combo Boxen aus verschiedenen Tabs gruppieren
            //Gruppe1
            group1.ComboBoxList.Add(cboBoxT1);
            group1.ComboBoxList.Add(cboBoxT2);
            //Gruppe2
            group2.ComboBoxList.Add(cboBox2T1);
            group2.ComboBoxList.Add(cboBox2T2);
            //Controls befüllen
            group1.Items = new List<string> { "A", "B" };

        }

        private void cboBoxTX_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (((ComboBox)sender).SelectedItem.ToString() == "A")
            {
                group2.Items = new List<string> { "A1", "A2" };
                group2.SelectedIndex =0;
            }
            if (((ComboBox)sender).SelectedItem.ToString() == "B")
            {
                group2.Items = new List<string> { "B1", "B2" };
                group2.SelectedIndex = 1;
            }
        }
    }

cboBoxTX_SelectedIndexChanged ist in beiden ComboBoxen als Event eingetragen.

In dem Beispiel wird bei A die 2. Gruppe auf A1, A2 geändert und A1 ausgewählt und bei B die 2. Gruppe auf B1, B2 geändert und B2 ausgewählt.
Dabei werden beide ComboBoxen aktualisiert.
 
Zuletzt bearbeitet: (Beispiel ein wenig verbessert)
Ich finde deinen ansatz doch sehr speziell, insbesondere im hinblick auch das Event das du da in der Form1 erstellst. Sowas sollte bei OOP sicher nicht der übergeordnete Container machen.

Das ist mein schneller Ansatz nach gut 5 Jahren C# OOP programmierung:freak:

Code:
    public class MultiControlState
    {
        public List<ComboBox> ControlList { get; set; } = new List<ComboBox>();
        private List<string> _itemList { get; set; } = new List<string>();

        public void Add(ComboBox inControl)
        {
            ControlList.Add(inControl);
            inControl.DataSource = _itemList;
            inControl.Tag = this;
        }

        private void SynchronizeControls(ComboBox inSender)
        {
            foreach (var tmpBox in ControlList.Where(inItem => inItem != inSender))
            {
                tmpBox.SelectedIndexChanged -= ComboBox_Changed;
                tmpBox.SelectedIndex = inSender?.SelectedIndex ?? 0;
                tmpBox.SelectedIndexChanged += ComboBox_Changed;
            }
        }

        private void ComboBox_Changed(object sender, EventArgs e)
        {
            SynchronizeControls(sender as ComboBox);
        }

        //von dir kopiert, da es sehr viel sinn macht und Entwickler faul sind.^^
        public List<string> Items
        {
            set
            {
                ControlList.ForEach(inItem => inItem.DataSource = value);
                _itemList = value;
            }
        }
    }

erstellt wird das ganze dann so (Form1):
Code:
  public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        MultiControlState State1 = new MultiControlState();
        MultiControlState State2 = new MultiControlState();

        private void Form1_Load(object sender, EventArgs e)
        {
            State1.Items = new List<string>
            {
                "10",
                "43",
                "76",
                "205",
            };

            for (var i = 0; i < 10; i++)
            {
                var tmpBox = new ComboBox { Location = new Point(0, i * 30) };
                pnl0.Controls.Add(tmpBox);
                State1.Add(tmpBox);
                tmpBox = new ComboBox { Location = new Point(0, i * 30) };
                pnl1.Controls.Add(tmpBox);
                State2.Add(tmpBox);
            }
            State2.Items = new List<string>
            {
                "adg",
                "sdafas",
                "sadfdas",
                "wetg",
            };
        }
    }

Die Idee dahinter: Dort wo du die Controls verwendest, sollst du dich nicht mehr darum kümmern müssen das das Control den gleichen Index/gleichen Wert hat wie alle anderen.
Das soll dein Tool für dich dann erledigen.
 
Danke, Threads Lösung gefällt mir wohl besser :)

Also wenn ich das richtig verstehe lege ich für jeden Tab einen Controller an der sich dann um sämtliche Comboboxen darin kümmert?. Oder meinst du für jeden n-ten Combobox einen Controller?
Und sobald ein Change Event aufgerufen wird wirds an Syncronize weitergeleitet.
Aber was ist jetzt mit "Where(inItem => inItem != inSender))" So wie ich das verstehe checkst du alle Boxen durch außer dem Eventsender. Und dann legst du von den andren Boxen auch noch irgendwas fest. (Die Selection von allen Boxen auf die Selection vom Sender?) Weshalb, mir ist nicht klar wozu?

Funktioniert das hinzufügen von Boxen wirklich so, oder ist das nur ein fixes Bsp? Werde doch wohl noch auf das Panel und den Tab zugreifen müssen?

In List Items... Warum legst du da nochmal die Datasource fest wenn die doch eh schon bei Add() bestimmt wird?

Und wozu itemList, wenn die Daten bei Load() eingegeben werden?

Kann es sein, dass ich das Beispiel ein bisschen blöd erklärt habe?...
Mal anders....Pizza
Jeder Tab steht für eine andere Größe für Pizzas. Und jede Combobox darin hält eine Liste von Zutaten, wobei egal welche Größe immer die selben Zutaten vorhanden sind. Der user kann jetz hergehn und die jeweiligen Zutaten aussuchen und diese werden dann für die jeweilige Größe/Tab gespeichert. Es wird nichts auf die anderen Tabs übertragen. Und es wird auch nichts auf die anderen Comboboxen innerhalb eines Tabs übertragen.
Aber sobald etwas - auch in mehreren Tabs - geändert wird, wird es gespeichert in Abhängigkeit vom Tab.

Hmm, na ob das eine bessere Erklärung ist :D
 
Halus schrieb:
Danke, Threads Lösung gefällt mir wohl besser :)

Also wenn ich das richtig verstehe lege ich für jeden Tab einen Controller an der sich dann um sämtliche Comboboxen darin kümmert?. Oder meinst du für jeden n-ten Combobox einen Controller?
Und sobald ein Change Event aufgerufen wird wirds an Syncronize weitergeleitet.
Aber was ist jetzt mit "Where(inItem => inItem != inSender))" So wie ich das verstehe checkst du alle Boxen durch außer dem Eventsender. Und dann legst du von den andren Boxen auch noch irgendwas fest. (Die Selection von allen Boxen auf die Selection vom Sender?) Weshalb, mir ist nicht klar wozu?
Win \o/ :evillol:

Nein, du hast einen Controler für die Zutaten, einen für die Grösse, einen Für die Sosenmenge, ... Darin sind jeweils die Entsprechenden Komboboxen angefügt.
Das hast du richtig verstanden.
Das ist LINQ, und die Funktion hast soweit richtig verstanden: Ich wähle dort aus der liste alle Elemente aus welche nicht dem Sender entsprechen.
Das ist Eventmagie: Da Ich auf das Change Event höre, dieses aber ändern möchte, muss Ich zuerst aufhören auf das Event zu hören in der entsprechden Kombobox, ansonsten verursache Ich einen Rekursiven aufruf. Wenn Ich den Index gewechselt habe, kann Ich wieder auf das Event hören damit Ich es mitbekommen wenn Jemand anderes an der Checkbox etwas ändert:freaky:
inSender?: Beim ? Operator handelt es sich um die 'Elvis-Tolle', dieser ersetzt ein: If(inSender !=null){soDomething}. Das gegenstück dazu ist der ?? operator, der gibt das Rechts von Ihm zurück, wenn der Linke ausdruck null ist.

PS: Das setzen eines Properties mit einem Wert (also [get;set;]="";) geht erst mit dem VS 2015^^, nur falls es mal irgendwo nicht klappen möchte, das Feature ist ganz neu.

Halus schrieb:
Funktioniert das hinzufügen von Boxen wirklich so, oder ist das nur ein fixes Bsp? Werde doch wohl noch auf das Panel und den Tab zugreifen müssen?
Ja, das Hinzufügen Funktioniert wirklich so.
Genau, auf ein Panel greife ich auch zu, pnl0 und pnl1, das wären dann bei dir die Orte im Tab wo die Kombobox hinzugefügt werden soll.

Halus schrieb:
In List Items... Warum legst du da nochmal die Datasource fest wenn die doch eh schon bei Add() bestimmt wird?
Simpel: So Spielt es keine Rolle ob du zuerst die Komboboxen anfügst oder zuerst die ItemListe anfügst, in beiden Fällen stimmt die Itemliste der Checkboxen.


Halus schrieb:
Und wozu itemList, wenn die Daten bei Load() eingegeben werden?
Damit Ich weiss welche Items vorhanden sind, bzw. damit Ich beim Add die Itemliste der Checkbox zuweisen kann.

Halus schrieb:
Kann es sein, dass ich das Beispiel ein bisschen blöd erklärt habe?...
Mal anders....Pizza
Jeder Tab steht für eine andere Größe für Pizzas. Und jede Combobox darin hält eine Liste von Zutaten, wobei egal welche Größe immer die selben Zutaten vorhanden sind. Der user kann jetz hergehn und die jeweiligen Zutaten aussuchen und diese werden dann für die jeweilige Größe/Tab gespeichert. Es wird nichts auf die anderen Tabs übertragen. Und es wird auch nichts auf die anderen Comboboxen innerhalb eines Tabs übertragen.
Aber sobald etwas - auch in mehreren Tabs - geändert wird, wird es gespeichert in Abhängigkeit vom Tab.

Hmm, na ob das eine bessere Erklärung ist :D
Mmmmm, Pizza.

Joah, das Ist ne ganz andere Anforderung.
In dem Fall hättest du mal sicher ein Container pro Tab, dieser müsste aber etwas anderes machen: Beim Speicher (also dem Change_Event) Muss er sich alle Controls zusammensuchen und deren Aktueller SelectedValue abspeichern.
Heisst konkret: Anstelle von SynchronizeControls müsstest du eine Funktion 'LoadStates' und 'SaveStates' haben, wobei du beim Event das Save aufrufst.
In den beiden Funktionen programmierst du dann aus, welche Daten von deinem Pizzaobject (das weiss ne Grösse, Zutaten, etc, und zu welchem Tab es gehört) in welche Kombobox kommen. Und um die Komboboxen zu identifizieren kannst du das 'Name' Property verwenden.
Ist dir Reflection ein Begriff?

Kan dort auch ein Beispiel machen, wenn es noch zu komplex ist.

Gruss Multi
 
Halus schrieb:
Jeder Tab steht für eine andere Größe für Pizzas.
Ich hätte dafür eine Combobox genommen. Würde ich mich spontan für eine andere Pizzagröße entscheiden, müsste ich bei deiner Version nochmal alle Zutaten festlegen.

Wenn du unbedingt mit Tabs arbeiten willst, mache daraus Pizzatypen ("Magerita", "Fungi", etc.) die der Benutzer selbst verwalten kann.
 
hmm was für einen Vorteil hätte ich jetzt genau wenn ich anstatt Tabs einfach noch eine Combobox nehme?
Bei meinem Szenario muss man auch nichts ändern..


Wie man ein Object hinzufügt ist klar, aber ich habe in deinem Bsp irgendwie das Object übersehen :D
Der Elvis Ausdruck ist trotzdem seltsam,... ein "?" nach einer Variable? :O
Ja, dass ich einen load und save benötige ist klar.
Wie würdest du dann die ganzen Panels/Tabs ansprechen? Müsste ich doch auch im Code generieren. Fünf Tabs so anzusprechen wäre mir irgendwie zu blöd.

Aber ich glaub ich werde das jetzt nochmal ändern, sodass anstatt Tabs eine zusätzliche Combobox für den Typ vorhanden ist. - Ich muss ja so oder so wo rauf klicken um alles zu sehen. Außerdem muss ich bei einer neuen "Pizzagröße"
Und wenn ich es so mache muss ich nur ein paar Comboboxen hinzufügen und keine Unterscheidung von Tabs bzw verschiedene Panels ansprechen - Ich mag keine GUIs >.<

Ich programmier das einmal aus und frage gegebenfalls noch einmal nach.

btw: Wird eigentlich WPF oder WinForms für einfache UIs empfohlen?
 
Hmm okay und schon bin ich hier.

Und zwar ist mir nicht klar wie ich eine Liste von Strings in der Combobox anzeige.

sizeBox.DataSource = this.sizeList;

sizeList ist einfach eine Liste von Strings.
Laut msdn sollte das ausreichen aber wenn ich die Zele meinem Project hinzufüge wird die GUI , ohne Fehlermeldung, nicht angezeigt.

Das selbe geschieht wenn ich eine Bindingsource verwende.

BindingSource bs = new BindingSource();
bs.DataSource = new List<string> { "test1", "test2" };
comboBox1.DataSource = bs;

VS wirft keinen Fehler, es zeigt mir nur die GUI nicht an.

Irgendeine Idee?
 
Zurück
Oben