Fragensteller
Combobox per Code auf einen bestimmten EIntrag festlegen (aka cascading comboboxes ?)

Frage
-
Mein Problem dürfte ein Spezialfall des Themas "cascading comboboxes" sein.
Ich lege mit Combobox1 einen Eintrag fest und lese aus den Daten des selektierten EIntrages den Fremdschlüssel.
Anhand dieses FK soll jetzt aus Combobox2 der korrespondierende Eintrag gewählt werden.
Das Problem wird im Netz wohl als cascading ... behandelt.
Dabei wird ausgehend von einer Selektion in CB1, die Menge an Einträgen in CB2 eingeschränkt, indem die Select-Anweisung neu gestaltet wird.
z. B wird in CB1 ein Land ausgewählt und anschliessend der Inhalt von CB2 so gestaltet, daß dort die Bundesländer/Staaten aufgelistet werden.
Wenn ich jetzt meine CB2 maximal einschränke - nämlich auf den DS mit meinem FK, dann kann ich nur noch diesen Eintrag auswählen. Das ginge aber zu weit.
Ich aber möchte nur, daß in CB2 ein Datensatz passend zum FK selektiert wird ohne die Auswahl einzuschränken. Praktisch eine Vorauswahl, die man dann aber ändern kann.
Nun enstpricht aber der FK in CB1 sicherlich nur selten dem SelectedIndex von CB2.
Wie könnnte ich dieses Problem lösen ?
Alle Antworten
-
Wie genau soll man dein Problem jetzt verstehen?
Ich gehe davon aus, dass du c# / WPF verwendest, weil du nichts anderes genannt hast.
Ich habe verstanden, dass man in der ComboBox1 ein Land wählt (Deutschland / Amerika etc.) und dann in der zweiten Box nur passende Vorschläge findet (Deutschland -> NRW, Berlin, ...). Habe ich das so richtig verstanden.
Wenn es so ist, würde ich Datenmodelle verwenden.
Du könntest z.B. folgendes Machen:
//Box1 und Box2 -> beides ComboBoxen void CreateList() { List<CountryModelItem> list = CreateList(...) //Sub-Methode this.Box1.ItemsSource = list; } void OnBox1SelctionChanged(...) { CountryModelItem item = (CountryModelItem)this.Box1.SelectedItem; this.Box2.ItemsSource = item.SubCountries; //Ist ja wieder eine Liste } //... Modell public class CountryModelItem { private string _name; private list<string> _subCountries; public CountryModellItem(int lngId /* Der Ländercode */) { //Name des Landes UND seiner Unterländer definieren... -> Stichwort Datenquelle (*.xml usw.) //_name und _subCountries belegen (dürfen nicht Null sein) } public string Name { get { return this._name; } } public string SubCountries { get { return this._subCountries; } } }
Eine andere Möglichkeit wäre die Benutzung von DataBindings in XAML (bei gleicher Verwendung von der Klasse
CountryModelItem
).
(C) 2014 Thomas Roskop
-
Wegen den Bindings, so würde es funktionieren:
XAML:
<ComboBox Name="CB1" MinWidth="150" SelectionChanged="CB1_SelectionChanged"/> <ComboBox Name="CB2" MinWidth="150" ItemsSource="{Binding ElementName=CB1, Path=SelectedItem.SubCountries}"/>
Und der passende c# Code:
Sollte selbsterklärend sein.private void Window_Loaded(object sender, RoutedEventArgs e) { List<MyCountry> c = new List<MyCountry>(); string[] names = { "Germany", "USA", "England", "Russia" }; foreach (var item in names) { c.Add(new MyCountry(item)); } this.CB1.ItemsSource = c; } private void CB1_SelectionChanged(object sender, SelectionChangedEventArgs e) {
//Wenn kein Binding verwendet wird, bitte Kommentar entfernen! //this.CB2.ItemsSource = ((MyCountry)this.CB1.SelectedItem).SubCountries; } ///***** public class MyCountry { private string _name; private List<string> _subCountries; public MyCountry(string name) { this._name = name; this._subCountries = new List<string>(); for (int i = 0; i < 7; i++) { this._subCountries.Add(name + " " + i.ToString()); //Unterkategoerein erstellen :) } } public string Name { get { return _name; } } public List<string> SubCountries { get { return _subCountries; } } public override string ToString() { return this._name; } }
(C) 2014 Thomas Roskop
- Bearbeitet Thomas Roskop Sonntag, 14. Dezember 2014 14:46 Notiz
-
Hallo,
mir geht es wie Thomas, ich bin mir nicht sicher ob ich verstanden habe, was du vor hast.Ich habe dich so verstanden, dass du in CB1 eine Liste von Ländern hast (Bspws.: Deutschland, Österreich, Schweiz) und in CB2 die einzelnen Bundeländer/Kantone (Bspws.: Sachsen, Berlin, Bayern,...,Bern, Luzern, ...)
Meine Lösung ist auch auf WPF ausgelegt:
Class MainWindow Dim l As New List(Of Item)() Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) l.Add(New Item() With {.Country = "Deutschland", .State = "Sachsen"}) l.Add(New Item() With {.Country = "Deutschland", .State = "Bayern"}) l.Add(New Item() With {.Country = "Deutschland", .State = "Berlin"}) l.Add(New Item() With {.Country = "Schweiz", .State = "Luzern"}) l.Add(New Item() With {.Country = "Schweiz", .State = "St. Gallen"}) l.Add(New Item() With {.Country = "Schweiz", .State = "Thurgau"}) 'usw.' 'Länder ausgeben cb1.ItemsSource = l.Select(Function(x) x.Country).Distinct() 'Die Bundesländer ausgeben cb2.ItemsSource = l.Select(Function(x) x.State) End Sub Private Sub ComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) cb2.SelectedItem = l.First(Function(x) x.Country = cb1.SelectedItem.ToString()).State End Sub End Class Public Class Item Public Property Country As String Public Property State As String End Class
Wobei hier dann natürlich auch Kombinationen wie Schweiz-Sachsen als Auswahl möglich wären. Ich vermute daher mal, das du eine weitere Einschränkung brauchst, wonach nur die passenden Bundesländer auftauchen:Dim l As New List(Of Item)() Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) 'l befüllen 'usw.' 'Länder ausgeben cb1.ItemsSource = l.Select(Function(x) x.Country).Distinct() cb2.IsEnabled = False End Sub Private Sub ComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) cb2.IsEnabled = True cb2.ItemsSource = l.Where(Function(x) x.Country = cb1.SelectedItem.ToString()).Select(Function(x) x.State) cb2.SelectedIndex = 0 End Sub
Wobei für letzteres Thomas Lösung vielleicht besser wäre, also die Bundesländer als Unterliste zu den Staaten. In meiner Signatur findest du einen C# -> VB.NET Konverter, falls du den Code nicht verstehst.
Tom Lambert - C# MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
Leider war ich zu unpräzise:
1. ich verwende VB.NET
2. alle Combo-Boxen werden aus Datasets befüllt , sind aber nicht an DB-Felder gebunden.
Wähle ich in CB1 einen Eintrag, dann lese ich per Code aus weiteren SPalten des gewählten Eintrages einen Fremdschlüssel.
In CB2 sind wiederum Datensätze aus einem anderen Dataset.
Ich möchte nun per Code in CB2 den Datensatz vorwählen, der den betreffenden FK hat. SelectedIndex dürfte nicht hinhauen, da der FK nur selten dem Index entsprechen dürfte.
-
Comboboxes aus Dataset füllen, wie geht das denn? Ein Dataset besteht aus zahlreichen Tabellen mit unterpunkten.
Vielleicht könntest du einen Teil deines Source Codes zeigen (wird ja kein Hochsicherheitsobjekt im Top-Secret-Niveau sein). Dann könnte man es besser nachvollziehen. Ein Code sagt mehr als tausend Worte...
- Bearbeitet Thomas Roskop Mittwoch, 17. Dezember 2014 19:04
-
Hi,
nachfolgend eine kleine Demo, wie man das machen könnte:Public Class Form1 Dim cb1 As New ComboBox With {.Top = 10, .Left = 10, _ .DisplayMember = "Text1", _ .ValueMember = "ID"} Dim cb2 As New ComboBox With {.Top = 40, .Left = 10, _ .DisplayMember = "Text2", _ .ValueMember = "ID"} Dim ds1 As New DataSet Dim WithEvents bs1 As New BindingSource Dim ds2 As DataSet Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load Me.Controls.AddRange(New Control() {cb2, cb1}) ' DataSet 1 mit CB1 ' With ds1 .Tables.Add(New DataTable With {.TableName = "Tab1"}) With .Tables(0).Columns .Add("ID", GetType(Integer)) .Add("Text1", GetType(String)) End With With .Tables(0) For i = 1 To 9 Dim dr = .NewRow dr(0) = i * 2 dr(1) = String.Format("Zeile {0}", i) .Rows.Add(dr) Next End With End With bs1.DataSource = ds1.Tables(0) cb1.DataSource = bs1 End Sub Private Sub CB1_Change() Handles bs1.PositionChanged ' DataSet 2 mit CB2 ' ds2 = New DataSet With ds2 .Tables.Add(New DataTable With {.TableName = "Tab2"}) With .Tables(0).Columns .Add("ID", GetType(Integer)) .Add("Text2", GetType(String)) End With With .Tables(0) For i = 1 To 9 Dim dr = .NewRow dr(0) = i dr(1) = String.Format("{0} {1}", CType(bs1.Current, DataRowView).Row(1), i) .Rows.Add(dr) Next End With End With cb2.DataSource = ds2.Tables(0) End Sub End Class
Es gibt aber auch noch andere Möglichkeiten, die u.U. besser sind. Dazu braucht man aber etwas mehr Information, was erreicht werden soll.
--
Peter
- Bearbeitet Peter Fleischer Donnerstag, 18. Dezember 2014 04:19
-
Wie genau soll man dein Problem jetzt verstehen?
Ich gehe davon aus, dass du c# / WPF verwendest, weil du nichts anderes genannt hast.
...
(C) 2014 Thomas Roskop
Hi Thomas,
wer im VB.NET-Forum postet, meint vielleicht auch VB.NET und nicht C#.NET.--
Peter -
Hallo,
ich habe nochmal versucht dein Vorhaben in WPF zu realisieren. In nachfolgendem Code sind cb1 und cb2 die ComboBoxen:Dim dt1 As New DataTable Dim dt2 As New DataTable Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) dt1.Columns.Add("Col1", GetType(String)) dt1.Columns.Add("Col2", GetType(String)) For i = 0 To 100 dt1.Rows.Add("Wert " & i, "Key" & i) Next dt2.Columns.Add("Col1", GetType(String)) dt2.Columns.Add("Col2", GetType(String)) For i = 0 To 100 'Die Werte entsprechen der 1. DataTable, aber der Key ist hier die erste Spalte' 'Damit die SelectedIndex verschieden sind, hier rückwärts' dt2.Rows.Add("Key" & (100 - i), "Wert " & (100 - i)) Next cb1.ItemsSource = dt1.AsDataView() cb2.ItemsSource = dt2.AsDataView() End Sub Private Sub cb1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles cb1.SelectionChanged Dim d = cb2.ItemsSource.OfType(Of DataRowView).FirstOrDefault(Function(x) x.Row.Field(Of String)("Col1") = CType(cb1.SelectedItem, DataRowView).Row("Col2").ToString) cb2.SelectedItem = d End Sub
Das Herzstück ist die Abfrage in der letzten Methode. Dort wird das erste Element aus cb2 heraus gesucht, welches in Col1 den Wert stehen hat, der in Col2 in cb1 ausgewählt wurde.
Wenn das noch immer nicht das ist, was du brauchst, fände ich ein Beispiel hilfreich. Also mit ein paar Beispieldaten, was in welchem Fall ausgewählt werden soll.
Nach wie vor steht die Frage ob du WPF oder WinForms verwendest.Tom Lambert - C# MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
C# und VB.NET verwenden zwar intern häufig die selben Klassen, es gibt aber wichtige Unterschiede. So kann man beispielsweise VBs Nothing nicht mit C#s null gleich setzen usw. Weiterhin machen auch die Konverter Fehler, die nicht immer ganz klar sind. Auch kann es sein das es unter VB.NET einen effizienteren Weg gibt, als den, den C# verwendet. (Besonders in älteren Versionen, wo VB.NET bei weitem nicht den Funktionsumfang von C# hatte)
Alles in Allem ist es daher besser direkt VB.NET Code zu posten.
Tom Lambert - C# MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets -
Hi Thomas,
nach Deiner Argumentation kann ich in Zukunft Quellcode in Lisp.NET posten, da es ja kein Problem ist, den MSIL-Code in die gewünschte Sprache zu rekompilieren. Ich denke, dass das für Anfänger kein optimaler Weg ist und wir doch bei den Themen bleiben sollten, die durch die Foren vorgegeben sind.Übrigens solltest Du mal Deinen VB.NET-Code laufen lassen und so korrigieren, dann man damit auch etwas anfangen kann.
--
Peter- Bearbeitet Peter Fleischer Mittwoch, 17. Dezember 2014 20:52
-
OK, ich gebe mich geschlagen.
Ich nahm an, dass der Code so gut portabel ist, da ich, als ich von Früher VB.Net auf c# (weil ich nebenbei c++ gemacht habe) gewechselt bin, keine Probleme hatte zu migrieren.
Und das es Lisp auch für .NET gibt, erstaunt mich und schockiert mich doch zugleich.
Ich wünsche noch einen schönen Abend.
© 2015 Thomas Roskop
-
Hi Thomas,
es gibt über 20 Programmiersprachen, für die es Compiler für die Erzeugung von MSIL-Code gibt, z.B. auch COBOL. Wieso schockiert Dich das?Die automatischen Konverter von Code aus MSIL in eine Programmiersprache sind nur bedingt für einfache Konstrukte brauchbar. Schau Dir mal an, was der VB.NET-Compiler zusätzlich alles einbaut, was C#.NET-Programmierer meist nicht machen, wie beispielsweise das Aufräumen von Objekten. Auch kenne ich keinen Konverter, der aus dem optimierten MSIL-Code wieder übersichtliche With-Konstrukte baut.
--
Peter
Meine Homepage mit Tipps und Tricks