Ik ben aan het experimenteren met DataSets en het koppelen hiervan aan de gebruikersinterface. De eenvoudige voorbeeldjes met een DataGrid control die je met honderden vindt op Internet krijg ik makkelijk werkende, maar vanzodra ik ietwat complexere zaken probeer kom ik vast te zitten.
Ik ga proberen mijn vragen te schetsen aan de hand van een eenvoudig voorbeeldprogramma.
Ik heb in een database een tabel "clients" welke een aantal kolommen heeft zoals "id" (primary key), "name", "street", "zip",... Nu werk ik aan een applicatie waarlangs ik de data in die tabel kan benadere, aanpassen, verwijderen en toevoegen.
Hiervoor heb ik een form (FormClient) welke een aantal invoercontrols zoals TextBoxes en CheckBoxes bevat. Op de form staan ook een Save, Reset en Cancel knop. De Save knop moet de eventueel veranderde rijgegevens opslaan in de database, de Reset knop herstelt de inhoud van de form naar de beginsituatie en Cancel negeert gewoon de wijzigingen.
Vraag 1.
Nu moeten we echter gaan herkennen of er gegevens veranderd zijn voor de Save knop.
Wanneer ik het iets anders aanpak werkt het wel min of meer:
Ik vind dit trouwens maar een rare werkwijze. M'n eerste (niet-werkende) poging vond ik veel logischer van opzet. Ik vraag me dan ook af of ik die niet werkende kan krijgen op een of andere manier. Ik ben vast iets stoms vergeten.
Vraag 2.
En eens we dit stukje werkende krijgen, moet de veranderde data nog in de database opgeslagen worden. Hier vraag ik me af of ik dit best manueel kan doen (dus controleren op Modified en dan zelf een "UPDATE clients SET ..." doen, of dit door de DataAdapter te laten gebeuren? De eerste methode ken ik, de tweede krijg ik niet werkende. Als iemand me dat kan uitleggen... En misschien ook beargumenteren waarom je voor de manuele methode zou kiezen of de DataAdapter methode? Mij lijkt de automatische methode logischer, minder code, minder kans op fouten,... Ik krijg het alleen nog niet werkende
Vraag 3.
Dan de Reset knop. Wanneer we daar op drukken moet de formdata terug naar de originele toestand gebracht worden (in de veronderstelling dat ondertussen al niet op Save geklikt is).
Vraag 4.
En als laatste belangrijke punt: Een nieuwe rij... Ik wil de form natuurlijk ook gebruiken voor het aanmaken van een nieuwe rij. Ik vraag me echter af hoe ik hierbij tewerk ga. Hoe krijg ik een nieuwe rij? Ik dacht zelf aan een "SELECT * FROM clients WHERE id = 0" waarbij ik weet dat id = 0 niet bestaat. Dit geeft dus geen rijen terug, maar als ik me niet vergis is het DataSet/DataTable object dan wel gezet. Middels een NewRow() ofzo zou ik dan een nieuwe lege rij kunnen aanmaken en die binden aan m'n controls. Maar dat lijkt me nogal een gekke werkwijze. Hoe doe je dit normaal?
Ik ga proberen mijn vragen te schetsen aan de hand van een eenvoudig voorbeeldprogramma.
Ik heb in een database een tabel "clients" welke een aantal kolommen heeft zoals "id" (primary key), "name", "street", "zip",... Nu werk ik aan een applicatie waarlangs ik de data in die tabel kan benadere, aanpassen, verwijderen en toevoegen.
Hiervoor heb ik een form (FormClient) welke een aantal invoercontrols zoals TextBoxes en CheckBoxes bevat. Op de form staan ook een Save, Reset en Cancel knop. De Save knop moet de eventueel veranderde rijgegevens opslaan in de database, de Reset knop herstelt de inhoud van de form naar de beginsituatie en Cancel negeert gewoon de wijzigingen.
C#:
Wanneer we vanuit een andere class de form aanroepen als volgt:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| public partial class FormClient : Form { DataSet ds = new DataSet(); public FormClient() { InitializeComponent(); } public FetchClient(int index) { NpqslConnection connection = new NpqslConnection("......"); connection.Open(); // Hier halen we de gewenste rij op. Omdat we altijd slechts één rij nodig hebben // gebruiken we een WHERE in de select in plaats van een RowFilter op het // DataSet object. Het is dan immers absurd om alle rijen op te halen als we er // toch maar één van gebruiken. string sqlstring = "SELECT * FROM clients WHERE id = " + index.ToString(); NpqslDataAdapter da = new NpqslDataAdapter(sqlstring, connection); // Vullen DataSet da.Fill(ds, "clients"); SetDataBindings(); } private SetDataBindings() { // DataBindings this.textBoxName.DataBindings.Add("Text", ds.Tables["clients"], "name"); this.textBoxStreet.DataBindings.Add("Text", ds.Tables["clients"], "street"); this.checkBoxVisible.DataBindings.Add("Checked", ds.Tables["clients"], "visible"); .. } } |
C#:
dan wordt automatisch de rij met 'id' 15 in de form geladen. Dat werkt perfect.1
2
| FormClient form = new FormClient(); form.FetchClient(15); |
Vraag 1.
Nu moeten we echter gaan herkennen of er gegevens veranderd zijn voor de Save knop.
C#:
Bij die if in buttonSave_Click gaat het echter al mis... RowState is altijd Unchanged, zelfs wanneer ik de data op de form compleet verander. Ik snap echter niet waarom... Moet ik eerst nog een bepaalde opdracht uitvoeren eer de RowState gezet wordt?1
2
3
4
5
6
7
8
9
10
11
12
13
| public partial class FormClient : Form { .. private void buttonSave_Click(object sender, EventArgs e) { // We kunnen steeds uitgaan van Rows[0] omdat er toch maar één rij in de DataTable zit. if (ds.Tables["clients"].Rows[0].RowState == DataRowState.Modified) { // Komt hier nooit? } } } |
Wanneer ik het iets anders aanpak werkt het wel min of meer:
C#:
Nu wordt de code in het if blok wel uitgevoerd wanneer ik op de Save knop druk en de formdata gewijzigd is. De functie ClientSupplier_RowChanged wordt wel tweemaal aangeroepen bij gewijzigde data (eerste keer e.Action = Change / .RowState = Modified wat normaal is, meteen gevolgd door een ietwat onverwachte e.Action = Commit / .RowState = Unchanged). Dat heeft geen invloed op de werking, maar ik vind het wel raar.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| public partial class FormClient : Form { .. public FetchClient(int index) { .. ds.Tables["clients"].RowChanged += new DataRowChangeEventHandler(Clients_RowChanged); } .. private void buttonSave_Click(object sender, EventArgs e) { ds.Tables["clients"].Rows[0].AcceptChanges(); } void Clients_RowChanged(object sender, DataRowChangeEventArgs e) { if (ds.Tables["clients"].Rows[0].RowState == DataRowState.Modified) { // Nu werkt het wel, maar is dit de juiste manier? } } } |
Ik vind dit trouwens maar een rare werkwijze. M'n eerste (niet-werkende) poging vond ik veel logischer van opzet. Ik vraag me dan ook af of ik die niet werkende kan krijgen op een of andere manier. Ik ben vast iets stoms vergeten.
Vraag 2.
En eens we dit stukje werkende krijgen, moet de veranderde data nog in de database opgeslagen worden. Hier vraag ik me af of ik dit best manueel kan doen (dus controleren op Modified en dan zelf een "UPDATE clients SET ..." doen, of dit door de DataAdapter te laten gebeuren? De eerste methode ken ik, de tweede krijg ik niet werkende. Als iemand me dat kan uitleggen... En misschien ook beargumenteren waarom je voor de manuele methode zou kiezen of de DataAdapter methode? Mij lijkt de automatische methode logischer, minder code, minder kans op fouten,... Ik krijg het alleen nog niet werkende
Vraag 3.
Dan de Reset knop. Wanneer we daar op drukken moet de formdata terug naar de originele toestand gebracht worden (in de veronderstelling dat ondertussen al niet op Save geklikt is).
C#:
Dit doet schijnbaar niets... Ik vermoed wel dat het DataSet object wel terug gereset wordt, maar de controls blijven hun (veranderde) waarden behouden. Hoe forceer ik dat alle gebonden controls mee geupdate worden en terug de begintoestand tonen?1
2
3
4
5
6
7
8
9
| public partial class FormClient : Form { .. private void buttonReset_Click(object sender, EventArgs e) { ds.Tables["clients"].Rows[0].RejectChanges(); } } |
Vraag 4.
En als laatste belangrijke punt: Een nieuwe rij... Ik wil de form natuurlijk ook gebruiken voor het aanmaken van een nieuwe rij. Ik vraag me echter af hoe ik hierbij tewerk ga. Hoe krijg ik een nieuwe rij? Ik dacht zelf aan een "SELECT * FROM clients WHERE id = 0" waarbij ik weet dat id = 0 niet bestaat. Dit geeft dus geen rijen terug, maar als ik me niet vergis is het DataSet/DataTable object dan wel gezet. Middels een NewRow() ofzo zou ik dan een nieuwe lege rij kunnen aanmaken en die binden aan m'n controls. Maar dat lijkt me nogal een gekke werkwijze. Hoe doe je dit normaal?