Änderungen erkennen und verwerfen

Wenn Sie ein Abonnement des Magazins 'DATENBANKENTWICKLER' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Änderungen erkennen und verwerfen

Unter Access können Sie beispielsweise mit der Escape-Taste die aktuellen ungespeicherten Änderungen an einem Datensatz verwerfen. Das Formular zeigt dann direkt die Daten an, die beim letzten Speichern in den gebundenen Feldern enthalten waren. Dieses Verhalten wollen wir auch für Fenster abbilden, die an ein Element gebunden sind und die Daten in einer Detailansicht anzeigen. Wir wollen erkennen, ob der Benutzer Änderungen an einem Datensatz vorgenommen hat und in Abhängigkeit davon eine Rückgängig-Schaltfläche aktivieren oder deaktivieren. Durch einen Klick auf die Schaltfläche sollen die vorherigen Werte wiederhergestellt werden. Wie das gelingt, zeigt der vorliegende Beitrag.

Ein WPF-Fenster bietet nicht automatisch eine Dirty-Eigenschaft wie ein gebundenes Access-Formular. Eine solche Funktion müssen wir selbst zu einem Fenster hinzufügen. Das Ergebnis dieses Artikels soll etwa wie in Bild 1 aussehen. Das Ändern eines Eintrags und das Verlassen dieses Eintrags sollen dafür sorgen, dass die Rückgängig-Schaltfläche aktiviert wird.

Aktivieren der Abbrechen-Schaltfläche nach einer Änderung

Bild 1: Aktivieren der Abbrechen-Schaltfläche nach einer Änderung

Betätigt der Benutzer dann die Rückgängig-Schaltfläche, soll der Datensatz auf die Version beim Laden zurückgesetzt werden (siehe Bild 2).

Zurücksetzen des Datensatzes nach Betätigen der Abbrechen-Schaltfläche

Bild 2: Zurücksetzen des Datensatzes nach Betätigen der Abbrechen-Schaltfläche

Vorbereitung

Wir nutzen wieder eine Anwendung auf Basis der Vorlage Visual Basic|Windows Desktop|WPF-App (.NET Framework). Dieser fügen wir ein neues Element des Typs ADO.NET Entity Data Model hinzu und legen für dieses den Namen BestellverwaltungContext fest. Dann fügen wir mit den Methoden der Datenbankdatei AccessZuWPFFormulare.accdb (siehe dortigen Readme-Bericht) ein Fenster auf Basis des Formulars frmKundendetails hinzu. Der Entwurf dieses Fensters sieht anschließend wie in Bild 3 aus.

Entwurf des Fensters frmKundendetails

Bild 3: Entwurf des Fensters frmKundendetails

Nun wollen wir erreichen, dass die Rückgängig-Schaltfläche beim Anzeigen des Fensters deaktiviert ist und erst aktiviert wird, wenn der Benutzer bei mindestens einem Feld eine Änderung vorgenommen hat. Wenn der Benutzer die dann aktivierte Schaltfläche Rückgängig betätigt, sollen die Änderungen rückgängig gemacht werden beziehungsweise die Werte zum Zeitpunkt des Öffnens des Fensters wieder eingestellt werden.

Speichern beim Schließen

Wir beginnen mit den einfachen Teil: Die Schaltfläche btnOK soll den geänderten Datensatz in die Datenbank übertragen. Das erledigen wir mit der folgenden Ergänzung der Ereignismethode btnOK_Click:

Private Sub cmdOK_Click(sender As Object, e As RoutedEventArgs)
     dbContext.SaveChanges()
     Close()
End Sub

Ereignis beim Ändern auslösen

Dann wollen wir ein Ereignis finden, das durch das Ändern eines Eintrags und das Verlassen des Feldes ausgelöst wird. Das scheint uns ausreichend zu sein – wir müssen ja nicht nach der Eingabe eines jeden Buchstaben prüfen, ob sich etwas geändert hat. Der erste Kandidat für eine passende Ereignismethode heißt LostFocus. Also fügen wir das Attribut zum XAML-Code hinzu.

<TextBox x:Name="txtFirma" Text="{Binding Kunde.Firma}" Height="21" Margin="118,31,0,0" Width="221" LostFocus="txtFirma_LostFocus"/>

Wenn wir LostFocus=" eingeben und dann die Tabulator-Taste betätigen, wird automatisch der Methodenname txtFirma_LostFocus vorgeschlagen. Durch Betätigen von F12 landen wir dann direkt bei dieser Methode im Code behind-Modul.

Mit der folgenden Testanweisung können wir gleich nach dem Starten der Anwendung testen, ob das Verlassen des Textfeldes txtFirma das Ereignis auslöst:

Private Sub txtFirma_LostFocus(sender As Object, e As RoutedEventArgs)
     MessageBox.Show("LostFocus")
End Sub

Herausfinden, ob eine Änderung vorliegt

Dieser Ereignismethode wollen wir nun Code hinzufügen, mit dem wir prüfen können, ob eine Änderung im aktuell angezeigten Element des Typs Kunde vorliegt.

Dazu benötigen wir zunächst einen Verweis auf den Namespace System.Data.Entity.Infrastructure, den wir im Kopf des Code behind-Moduls einfügen. System.Data.Entity benötigen wir später auch noch:

Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity

Danach erweitern wir die Ereignismethode wie folgt: Wir fügen eine Variable des Typs DbEntityEntry namens GeaendertesElement hinzu. Dieses füllen wir über die Auflistung Entries des ChangeTracker-Objekts, das dem aktuell in Kunde gespeicherten Element entspricht. Dieser Auflistung entnehmen wir mit der First-Eigenschaft das erste und einzige Element. Dann geben wir in einer weiteren MessageBox den aktuellen Zustand dieses Elements aus, den wir über die Eigenschaft State ermitteln:

Private Sub txtFirma_LostFocus(sender As Object, e As RoutedEventArgs)
     Dim GeaendertesElement As DbEntityEntry
     GeaendertesElement = dbContext.ChangeTracker.Entries().Where(Function(x) x.Entity Is Kunde).First()
     MessageBox.Show("Element geändert? " + GeaendertesElement.State.ToString())
End Sub

Das Ergebnis sieht wie in Bild 4 aus. Der Zustand Unchanged entspricht nun nicht dem, was wir erwartet haben.

Aktueller Zustand des Elements

Bild 4: Aktueller Zustand des Elements

Erst, wenn wir zum geänderten Datensatz zurückwechseln und dann erneut das Ereignis LostFocus auslösen, erscheint der Wert Modified im Meldungsfenster (siehe Bild 5).

Aktueller Zustand des Elements, diesmal geändert

Bild 5: Aktueller Zustand des Elements, diesmal geändert

Offensichtlich ist der neue Wert im zugrundeliegenden Element Kunde beim Auslösen des LostFocus-Ereignisses noch nicht angekommen.

Also ist LostFocus wohl nicht unser Ereignis. Dementsprechend kommen wir auch mit einem Ereignis wie TextChanged nicht weiter, denn möglicherweise benötigen wir einen abgeschlossenen Fokuswechsel, um die Änderung in das Kunde-Element zu übertragen.

Änderung nach Fokuserhalt

Schauen wir uns also an, ob unsere Idee stimmt. Dazu fügen wir testweise dem nächsten Textfeld, hier txtVorname, das Attribut GotFocus hinzu:

<TextBox x:Name="txtVorname" Text="{Binding Kunde.Vorname}" Height="21" Margin="118,58,0,0" Width="221" GotFocus="txtVorname_GotFocus"/>

Die Ereignismethode füllen wir wie folgt:

Private Sub txtVorname_GotFocus(sender As Object, e As RoutedEventArgs)
     Dim GeaendertesElement As DbEntityEntry

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.