Laden...

DataGridView: Master/Detail über ComboBox

Letzter Beitrag vor 14 Jahren 20 Posts 28.852 Views
DataGridView: Master/Detail über ComboBox

Hallo,
über die Doku und andere Diskussionen ist ja klar: DataGridView kann nur Daten aus einer Tabelle anzeigen. Für eine Master/Detail-Verbindung kann jedoch DataGridViewComboBoxColumn verwendet werden:

public JThomas.DGW.Data.TypisiertesDataSet ds;
private System.Windows.Forms.BindingSource Quelle;
private System.Windows.Forms.DataGridView Grid;
//  zunächst alle Spalten der Tabelle "Musik" bereitstellen
Grid.AutoGenerateColumns = true;
Grid.SuspendLayout();
//  Datenverbindung herstellen
Quelle.DataSource = ds;
Quelle.DataMember = "Musik";   
Grid.DataSource = Quelle;
			
//  Variablen für die folgenden Anpassungen
DataGridViewComboBoxColumn combo;
int i1;

//  registriere das Feld "Sendung_ID" der Tabelle "Musik"			
i1 = ds.Musik.Columns.IndexOf("Sendung_ID");
//  entferne es aus der Liste der Spalten
Grid.Columns.Remove("Sendung_ID");

//  erzeuge eine neue Spalte mit Querverweis auf Tabelle Sendung
combo = new DataGridViewComboBoxColumn();
combo.DataSource = ds.Sendung;
combo.ValueMember = "ID";
combo.DisplayMember = "Sendung_Name";
combo.HeaderText = "Sendung";
combo.Width = 100;
//  unterdrücke die DropDown-Bitmap
combo.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
//  füge diese Spalte an der originalen Stelle ein
Grid.Columns.Insert(i1, combo);

**Grundsätzlich funktioniert das. **Aber es gibt noch eine Reihe Schönheitsfehler:*Das **DisplayMember **wird nicht angezeigt, sondern ein weißes Feld. Wenn ich 2x in das Feld klicke (kein Doppelklick!), dann wird der korrekte Wert dargestellt; aber es kommt auch die DropDown-Bitmap. *Durch AutoSizeColumnsMode = AllCellsExceptHeader wird die Spaltenbreite eigentlich sauber dargestellt. Aber combo.Width = 100 kann nicht berücksichtigt werden. *Ich habe AllowUserToResizeColumns = true sowie RowHeadersWidthSizeMode = EnableResizing festgelegt; aber es kann nur die "erste" Spalte RowHeaders verändert werden. *In gleicher Weise möchte ich auch DataGridViewCheckBoxColumn (für ein nicht-bool-Feld) einbinden; aber auch dort wird nichts angezeigt.

Habt Ihr Ideen, wie man diese Unsauberkeiten beseitigen kann?

Herzlichsten Dank! Jürgen

Haaalllooo!

Kann mir wirklich niemand helfen, die Schönheitsfehler zu beseitigen? Jürgen

Setz mal:
combo.DataPropertyName = "ID";

Danke, Noodles,

Dein Hinweis hat mich wenigstens für das erste Problem in die richtige Richtung gebracht. Der korrekte Befehl lautet:

//  Verweis auf den Feldnamen der Tabelle, die im GridView angezeigt wird
combo.DataPropertyName = "Sendung_ID";

Jetzt fehlen noch die folgenden Probleme:*Spaltenbreite manuell festlegen: Durch AutoSizeColumnsMode = AllCellsExceptHeader wird die Spaltenbreite eigentlich sauber dargestellt. Aber z.B. combo.Width = 100 wird nicht berücksichtigt. Muss ich wirklich auf AutoSize verzichten und alle Spalten einzeln festlegen? *Spaltenbreite zur Laufzeit per Maus ändern: Ich habe AllowUserToResizeColumns = true sowie RowHeadersWidthSizeMode = EnableResizing festgelegt; aber es kann nur die "erste" Spalte RowHeaders verändert werden. *CheckBox verwenden: In gleicher Weise möchte ich auch DataGridViewCheckBoxColumn (für ein nicht-bool-Feld) einbinden; aber auch dort wird nichts angezeigt.

Zum letzten Problem: Weil Firebird keine "echten" bool-Felder kennt, habe ich für die Tabelle ein int-Feld mit den Werten 0/1 vorgesehen; im Grid möchte ich es als Checkbox anzeigen, es ist aber nichts zu sehen:

check = new DataGridViewCheckBoxColumn();
check.HeaderText = "?";
check.DataPropertyName = "Problem";
check.TrueValue = 1;
check.FalseValue = 0;
Grid.Columns.Insert(i1, check);

Kann ich auch für diese Probleme noch Tipps erhalten? Danke! Jürgen

Hi, deine Anleitung hat mir schon sehr geholfen, aber ich muss den Thread noch einmal ausgraben.
//bin noch recht un erfahren mit Net und C#

Ist es möglich den angezeigten Wert in der Combobox vorzuwählen für jede Zeile?
Also das die Combobox immer mit den Zahlen 1-10 gefüllt ist und je nachdem halt die 4 oder 8 usw. vorselektiert ist?

...Ist es möglich den angezeigten Wert in der Combobox vorzuwählen für jede Zeile?
Also das die Combobox immer mit den Zahlen 1-10 gefüllt ist und je nachdem halt die 4 oder 8 usw. vorselektiert ist?

Du kannst wie gehabt per .SelectedIndex eine Auswahl vornehmen. Ob .Text oder .SelectedValue auch funktioniert, kann ich dir spontan nicht sagen. Im Zweifelsfalle musst du halt die .Items durchgehen und den Index herausziehen (ist nicht schön, kostet aber auch nur wenig Leistung bei einer Auswahl < 100 Items).

Halloo?

Die Rede ist doch von einer ComboColumn, odr?
Also eine DataGridView-Spalte, deren Zellen als Combo editierbar sind. Die Zellen zeigen natürlich den Wert an, der in den Datensätzen steht, da kann man nix vor-anwählen.
Wenn die Zelle "8" anzeigen soll, musste "8" in den entsprechenden Datesatz schreiben.

Der frühe Apfel fängt den Wurm.

So richtig verstehe ich das noch nicht, daher erzähle ich mal was ich da mache:

Ich möchte per C#.Net Programm auf eine MySql Datenbank zu greifen. In einem DataGridView sollen die Inhalte angezeigt werden. Es handelt sich um Arbeitsgänge. Der Zugriff erfolgt auf 2 Tabellen der Datenbank. Aus der einen Tabelle kommen Daten wie Bezeichnung, Nummer, Info usw., diese kommen im DataGridView in normale Textfelder. Jedoch möchte ich die Normen zu den Arbeitsgängen in eine ComboBoxColumn packen, diese kommen aus einer anderen Tabelle in der Datenbank (verknüpft über die ID). Die ComboBox soll halt alle Normen anzeigen und die Entsprechenden vorselektiert haben.
Finde es halt schön, diese Daten alle in einer Tabelle (DataGridView) zu visualisieren und ändern zu können. Der MySqlDataAdapter und Co. ist da ja sehr komfortabel.

Wie muss ich theoretisch vorgehen um diese ComboBoxen richtig zu füllen?
Alles in ein DataTable packen und an dieses an das GataGridView packen geht, dann steht die Norm als ID im Textfeld. Mit einem INNER JOIN kann ich auch den richtigen Wert ausgeben, aber wie ich da mit einer ComboBox verfahren muss, ist mir noch unklar.
--> Muss ich eine zweite DataTable laden, nur mit den Norm Werten und diese dann als DataSource an die ensprechende ComboBox binden? Bisher bekomme ich da immer eine Exception --> "System.Argument.Exception: Der DataGridViewComboBoxCelle-Wert ist ungültig. Behandeln Sie das DataError-Ergeinis, um dieses Standarddialogfeld zu ersetzen."

In der DB müssen 2 Tabellen angelegt sein: Norm und Arbeitsgang.
Mit einer 1:n - Relation von Norm.NormID -> Arbeitsgang.NormID

daraus ein typisiertes Dataset generieren, bei dem die Verhältnisse ebenso sind (2 Tabellen, eine DataRelation).

Dann ein DGV an die Arbeitsgang-Tabelle des Datasets binden. Im DGV die an Arbeitsgang.NormID gebundene TextboxColumn durch eine ComboboxColumn austauschen, die ihre Display-Werte aus der Norm-Tabelle des Datasets holt.

Das ganze nenne ich Master-Detail - View, weil die Norm als Detail der Arbeitsgang-Datensätze aufgefasst wird.

Ein Tut findet sich dazu auf Movie-Tuts

Vorraussetzung ist halt, du kriegst es gebacken, ein typisiertes Dataset zu erstellen, und zu befüllen.

Prinzipiell wird das gezeigt im Tut "Datenbank in 10 Minuten", aber auf Access aufgebaut.

Außerdem müssteste den VB-Code verstehen, aber das sind nur ca. 20 Zeilen.

Der frühe Apfel fängt den Wurm.

Danke, das hört sich doch schon mal ganz gut an. Bin froh, dass das geht.
Werde mal die Vids studieren und habe ja nun auch neue Schlagworte zum suchen.

Danke noch mal.

//edit

bekomms nicht hin, selber Fehler wie oben
Habe mich ans Video-Tut gehalten
Typisiertes DataSet erstellen war nicht schwer.

//edit2

jetzt klappts wie gewünscht ... 🙂

Hallo Leute,

Hab alles so wie beschrieben gemacht funktioniert soweit auch recht gut.

Mein Problem ist jetzt das, dass wenn ich das Dataset wieder in die db schreib will also halt aktualisieren dann birngt er mir bei den spalten wo ich die combox hinzugefügt und die originalspalte gelöscht habe ne fehlermeldung (wert null kann nicht eingefügt werden) kanns sein, dass durch das entfernen und hinzufügen der spalte das nicht im dataset übernommen wird sondern was unabhängiges ist?

Was kann ich dagegen tun?

Lg Tom

Hallo Tom,

das Problem verstehe ich so nicht. Bei meinem "Haupt-Code" wurde eine Spalte im DataGridView gelöscht, aber keine Spalte in der DataTable. Das Speichern in der Datenbank muss sich natürlich immer auf die Spalten in der DataTable beziehen, und dort wird überhaupt nichts geändert.

Jürgen

Hei Juetho,

Ja wie du sagst im Datatable wird normlerweise gar nichts verändert stimmt soweit ja auch. Mein Code sieht so aus also wie deiner halt:


BS.DataSource = DS.Tables[0];
            stp_navigator.BindingSource = BS;
            dgv_data.DataSource = BS;   
  
                        
            DataGridViewComboBoxColumn combo_1=new DataGridViewComboBoxColumn();
            DataGridViewComboBoxColumn combo_2 = new DataGridViewComboBoxColumn();

            int index_1 = 0, index_2 = 1;

            col_name_1 = dgv_data.Columns[0].Name;
            col_name_2 = dgv_data.Columns[1].Name;

            dgv_data.Columns.Remove(dgv_data.Columns[0].Name);
            dgv_data.Columns.Remove(dgv_data.Columns[0].Name);

            combo_1.DataSource = DS_Box1.Tables[0];
            combo_2.DataSource = DS_Box2.Tables[0];

            combo_1.DisplayMember = DS_Box1.Tables[0].Columns[0].ColumnName;
            combo_2.DisplayMember = DS_Box2.Tables[0].Columns[0].ColumnName;
    
            combo_1.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox;
            combo_2.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;

            combo_1.HeaderText = col_name_1;
            combo_2.HeaderText = col_name_2;

            dgv_data.Columns.Insert(index_1, combo_1);
            dgv_data.Columns.Insert(index_2, combo_2);

Nur wenn ich jetzt das DataSet updaten will kommt die Fehlermeldung. Und wenn ich mein Dataset anschau steht in den zwei Columns mit den ComboBoxen nichts drinnen und in der normalen column der eingetragene wert.

Versteh nicht ganz warum er die Werte von diesen zwei Columns nicht übernimmt. Muss ich da irgendwas uptdaten oder so? Ein Refresch der DataGridView hilft nichts.

Lg

Und wo steht ValueMember? Jürgen

Auf welche Spalte (bzw. Tabelle) bezieht sich das? auf die mit der ich die Combobox befülle oder auf die bei der ich was ändern also die Werte von der Combobox verwenden will? Versteh das mit dem ValueMember noch nicht ganz.

Hat das mit meinem Problem zu tun?

Lg Tom

Jede ComboboxColumn braucht eine Datasource, das ist eine dem Datagridview übergeordnete Tabelle.
Dann einen Displaymember, diese Werte werden dann in der Textbox der Combo zu lesen sein.
Dann einen ValueMember (übliweise der Primkey). Diese Werte werden in die DataProperty der Datagridview-Tabelle geschrieben.

Hi Juetho!
Da fehlt ja auch noch der DataPropertyName, odr?.

Welches ist der ForeignKey der DGV-Tabelle.

Ich empfehle solche Fummeleien im Designer zu fummeln, wie in meine Tuts gezeigt.

Der frühe Apfel fängt den Wurm.

Hallo erfinder,

Der Primkey besteht aus 3 Spalten (In der Tabelle sind auch nur drei Spalten vorhanen) Die ersten zwei sollen von einer anderen Tabelle (wegen dem die Combobox) gefüllt werden können (dass eben die verfügbaren werte über die combo ausgewählt werden können).

Bei den zwei tabellen mit denen ich die combo1 und combo2 fülle ist ein primkey vorhanden. jeweils die erste spalte.

Foreign Key hab ich jetzt keinen definiert.

Ich steig jetzt voll aus. Was genau kann ich jetzt tun damit das funzt. biite hilfe

danke tom

Zusatzinforamtion:

Vielleicht mach ich da was anderes falsch.

Ich lade mir die Tabelle für die Datagridview in ein dataset. und die Comboboxen lade ich in 2 extra datasets(könnte alle drei in eines laden ist aber abhängig von einer db methode die nicht von mir ist). also die combos beschreibe ich mit den 2 extra datasets (DS_BOX1, DS_BOX2) und die datagridview mit dem hauptdatases(DS)

Das Problem is ja eigentlich das. In den Comboboxen wird alles richtig angezeigt. Auswahl funktioniert einwandfrei. Nur irgendwie übernimmt er im Dataset (DS das für die DataGridView ist) die Werte nicht. Das ist das eigentliche Problem

Ah ja, mit dieser Erläuterung ist klar, dass es unklar ist. Mit meiner ganzen Überlegung bin ich davon ausgegangen (analog zum Beitrag von ErfinderDesRades), dass sich beide DataTables im selben DataSet (!) befinden und durch eine DataRelation (= ForeignKey) verbunden sind. Auch nehme ich an, dass das NET Framework mit seiner Konstruktion des DGV sich das ganz genauso gedacht hat.

Wenn du alle DataTables in verschiedenen DataSets hältst, dann wüsste ich nicht, wie man das übernehmen kann.

Da fehlt ja auch noch der DataPropertyName, odr?.
Welches ist der ForeignKey der DGV-Tabelle.

Stimmt, dieser Fehler ist eigentlich noch wichtiger als der von mir genannte mit ValueMember.

Jürgen

Hallo juetho und erfinder,

Hab jetzt alles gemacht und hat funktioniert. Habe ForeignKeys vergeben und die DataPropertyName zugewiesen. So funktioniert jetzt alles.

Und natürlich die 3 Tabellen in ein DATASET geschreiben.

Danke für eure Hilfe!!!!!!

Lg Tom