Validieren mit VB und EDM

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.

Validieren mit VB und EDM

Im Artikel EDM: Validieren von Entitäten mit IDataErrorInfo haben wir bereits erläutert, wie Sie einer auf dem Entity Framework basierenden Anwendung eine Validierung hinzufügen können. Im vorliegenden Artikel schauen wir uns an, wie Sie die Validierung nicht unter C#, sondern unter VB realisieren und welche Änderungen in den Methoden zum Migrieren einer Access-Anwendung nach WPF/EDM notwendig sind, um die Validierung vorzubereiten oder gegebenenfalls auch direkt umzusetzen.

Für die Programmierung einer Validierung in den Klassen des Entity Data Models und der mit WPF definierten Benutzeroberfläche sind einige Schritte auf den verschiedenen Ebenen erforderlich. Der erste ist, die Entitätsklassen mit der Implementierung der Schnittstelle IDataErrorInfo zu versehen, die zweite die Definition der Anzeige der Validierungsmeldungen in der Benutzeroberfläche – und das geschieht durch entsprechende WPF-Elemente.

IDataErrorInfo – wo implementieren?

Im oben genannten Artikel haben wir unter C# die Schnittstelle IDataErrorInfo für partielle Klassen implementiert. Das bedeutet, dass wir zwei gleichnamige Klassen programmiert haben, wobei wir in der einen die Definition der Properties und in der anderen die Implementierung der Schnittstelle IDataErrorInfo eingefügt haben. Warum haben wir das dort so gemacht? Weil wir den Code für die Entitätsklassen aus dem Datenmodell der Datenbank generiert haben, was bedeutet, dass wir bei Änderungen des Datenmodells auch die Entitätsklassen neu generieren mussten. Wenn wir dann die Implementierung der Schnittstelle IDataErrorInfo in der automatisch generierten Entitätsklasse programmiert hätte, wäre diese bei jeder Aktualisierung wieder überschrieben worden. Deshalb haben wir diese Definition direkt in eigenen, partiellen Klassen programmiert. Partielle Klasse bedeutet dabei, dass wir einfach eine weitere Klasse gleichen Namens mit dem Schlüsselwort Partial erstellt und dort die Elemente für die Implementierung der Schnittstelle hineingeschrieben haben. Die Klassenbezeichnungen der per Generator erzeugten Entitätsklassen wurden dabei praktischerweise bereits mit dem Schlüsselwort Partial ausgezeichnet – was keinen Nachteil bringt, auch wenn es keine weiteren partiellen Klassen mehr gibt.

Nun verwenden wir in der aktuell in diesem Magazin beschriebenen Vorgehensweise eine Access-Datenbank als Vorlage für die Erstellung eines Entity Data Models und auch für die Erstellung von Fenstern auf Basis der Formulare der Access-Datenbank. Wir könnten also theoretisch auch gleich die Implementierung der Schnittstelle IDataErrorInfo in die Entitätsklassen schreiben – und hier direkt die im Datenmodell festgelegten Restriktionen wie keine Nullwerte et cetera eintragen. Allerdings kann es auch hier sein, dass Sie die Implementierung der Schnittstelle IDataErrorInfo selbst anpassen wollen. Also werden wir auch hier mit partiellen Klassen arbeiten und per Parameter beim Erstellen des Entity Data Models auf Basis der Tabellen der Access-Datenbank die Möglichkeit bieten, entweder nur das Entity Data Model zu erstellen oder auch noch die partiellen Klassen mit den Validierungsfunktionen neu zu generieren. Zunächst schauen wir uns allerdings an, wie die partiellen Klassen unter VB überhaupt aussehen sollen.

Partielle Klasse mit IDataErrorInfo erstellen

Das Anlegen der zusätzlichen partiellen Klasse etwa für die Klasse Kunde erfordert zwei Schritte. Als Erstes fügen wir der eigentlichen Klasse das Schlüsselwort Partial als erstes Schlüsselwort noch vor Public hinzu:

<Table("Kunden")>
Partial Public Class Kunde
     ...
End Class

Danach können wir die zweite partielle Klasse erstellen:

Partial Public Class Kunde
     ...
End Class

Für diese Klasse wollen wir nun die Schnittstelle IDataErrorInfo implementieren. Dazu benötigen wir zunächst die Bibliothek System.ComponentModel, die wir per Imports-Anweisung hinzufügen:

Imports System.ComponentModel

Danach fügen Sie in der Zeile unter der Klassendefinition die Anweisung zur Implementierung der Schnittstelle hinzu:

Partial Public Class Kunde
     Implements IDataErrorInfo

...

Wenn Sie mit der rechten Maustaste auf IDataErrInfo klicken, erscheint der Kontextmenü-Eintrag Schnellaktionen und Refactorings..., den Sie anklicken und danach den nun erscheinenden Befehl Schnittstelle implementieren anklicken (siehe Bild 1).

Automatisches Implementieren der Schnittstelle

Bild 1: Automatisches Implementieren der Schnittstelle

Das füllt die Klasse mit den beiden Properties Item und [Error]:

Partial Public Class Kunde
     Implements IDataErrorInfo
     Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
         Get
             Throw New NotImplementedException()
         End Get
     End Property
     Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
         Get
             Throw New NotImplementedException()
         End Get
     End Property
End Class

Uns interessiert dabei speziell die Property namens Item. Diese liefert mit dem Parameter columnName den Namen des Feldes, für das eine Validierung durchgeführt werden soll. Um eine Validierung für das Feld Vorname durchzuführen, die prüfen soll, ob das Feld Vorname leer ist, fügen Sie die folgende Select Case-Bedingung mit dem Case-Zweig für den Feldnamen Vorname hinzu. Die Prüfung stellt mit der Methode IsNullOrEmpty fest, ob Vorname leer ist. In diesem Fall wird der Rückgabeparameter strErrorMessage mit einer entsprechenden Fehlermeldung gefüllt:

Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
     Get
         Dim strErrorMessage As String = ""
         Select Case columnName
             Case "Vorname"
                 If (String.IsNullOrEmpty(Vorname)) Then
                     strErrorMessage = "Bitte geben Sie einen Vornamen ein."
                 End If
         End Select
         Return strErrorMessage
     End Get
End Property

Nun stellt sich nur noch die Frage, wodurch diese Methode aufgerufen wird und wie wir die Fehlermeldung aus strErrorMessage verarbeiten.

Auslösen der Validierung

Dass für das Feld Vorname eine Validierung ausgelöst wird, legen wir in der Definition des an dieses Feld gebundenen Steuerelements fest, in diesem Fall des Textfeldes txtVorname. Hier erweitern wir den Inhalt der Bindung für das Attribut Text um ValidatesOnDataErrors=True:

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

Damit allein wird nun bereits die Validierung ausgelöst, das heißt, wenn das Feld Vorname angezeigt wird, ruft dies auch die Methode Item der Implementierung von IDataErrorInfo auf. Das Ergebnis ist allerdings noch keine Anzeige der Meldung, sondern zunächst nur das rote Einrahmen des Feldes, das nicht erfolgreich validiert werden konnte (siehe Bild 2).

Die Validierung funktioniert bereits.

Bild 2: Die Validierung funktioniert bereits.

Wir wollen allerdings noch konkretere Hinweise auf den Grund für die farbige Markierung liefern, damit der Benutzer weiß, was zu tun ist. Also verwenden wir den Setter, den wir auch schon im oben genannten Artikel genutzt haben, um die Fehlermeldung an entsprechender Stelle unterzubringen. Dazu erweitern wir den Code für das Style-Element in der .xaml-Datei wie folgt:

<Style TargetType="{x:Type TextBox}">
     ...
     <Setter Property="Validation.ErrorTemplate">
         <Setter.Value>
             <ControlTemplate>
                 <DockPanel>
                     <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10" ToolTip="{Binding ElementName=customAdorner,                             Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                         <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white" />
                     </Border>
                     <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                         <Border BorderBrush="red" BorderThickness="1" />
                     </AdornedElementPlaceholder>
                 </DockPanel>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
</Style>

Danach erscheint die Meldung, die wir für das Feld Vorname definiert haben, wie in Bild 3.

Validierung mit Hinweismeldung

Bild 3: Validierung mit Hinweismeldung

Validierung von Kombinationsfeldern

Für die Validierung des Kombinationsfeldes Anrede fügen Sie in der .xaml-Datei für die Definition des Style-Elements für den Typ ComboBox genau die gleiche Property namens Validation.ErrorTemplate wie für den Typ TextBox hinzu:

<Style TargetType="{x:Type ComboBox}">
     ...
     <Setter Property="Validation.ErrorTemplate">
     ...
     </Setter>
</Style>

Außerdem passen Sie in der Definition des ComboBox-Elements das Attribut SelectedItem so an, dass die Bindung mit der Option ValidatesOnDataErrors=True erfolgt:

<ComboBox x:Name="cboAnredeID" Padding="0" Height="21" Margin="118,111,0,0" Width="78"
     ItemsSource = "{Binding Anreden}"
     SelectedItem = "{Binding Kunde.Anrede, ValidatesOnDataErrors=True}"
     ...
</ComboBox>

Schließlich erweitern wir die Implementierung der Schnittstelle IDataErrorInfo noch um einen entsprechenden Case-Zweig. Hier verwenden wir allerdings die IsNothing-Funktion, um zu prüfen, ob für die Eigenschaft Anrede eines der Elemente der Auflistung Anreden zugewiesen wurde:

Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
     Get
         Dim strErrorMessage As String = ""
         Select Case columnName
             ...
             Case "Anrede"
                 If (IsNothing(Anrede)) Then
                     strErrorMessage = "Bitte wählen Sie eine Anrede aus."
                 End If
         End Select
         Return strErrorMessage
     End Get
End Property

Verlassen des Datensatzes ohne erfolgreiche Validierung unterbinden

Nun müssen wir noch sicherstellen, dass der Benutzer den Datensatz nicht verlassen kann, ohne dass alle erforderlichen Daten eingegeben wurden. Dazu haben wir im oben genannten Artikel eine Technik kennengelernt, die auf den Ergebnissen unserer Validierung aufbaut und davon abhängig Steuer­elemente aktiviert oder deaktiviert. Dabei haben wir für die betroffene Schaltfläche DataTrigger-Element als Style-Elemente festgelegt und für jeden Trigger eine Bedingung formuliert, unter der eine bestimmte Eigenschaft auf einen Wert eingestellt werden soll – in diesem Fall etwa der Wert der Validierung eines oder mehrerer gebundener Steuer­elemente und damit verbunden die Eigenschaft IsEnabled der zu deaktivierenden Schaltfläche.

Daraus leiten wir für unsere Steuer­elemente, hier etwa für die Schaltfläche btnErster, die entsprechende Style-Definition ab. Die Style.Triggers-Auflistung nimmt dabei mehrere DataTrigger-Elemente auf. Diese referenzieren jeweils die Eigenschaft Validation.HasError eines der Steuer­elemente, in der nachfolgenden verkürzten Form etwa die Textfelder txtFirma oder txtEMail. Ist Validation.HasError den Wert True, wird der eingefasste Setter aktiviert, der hier den Wert der Eigenschaft IsEnabled auf True einstellt:

<Button x:Name="btnErster" Content="<<" Click="btnErster_Click" Width="25" Margin="2,2,2,2">
     <Button.Style>

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.

Neues aus unseren Magazinen
Listenfeld: Reihenfolge mehrerer Einträge...

Wir haben bereits in mehreren Beiträgen beschrieben, wie Sie die individuelle Reihenfolge von Elementen einer Tabelle über den Inhalt eines Feldes etwa namens »ReihenfolgeID« einstellen können –... [mehr]

Diagramme mit gefilterten Daten

In Ausgabe 2/2019 haben wir in zwei Artikeln die modernen Diagramme von Access vorgestellt. Im vorliegenen Beitrag zeigen wir Ihnen, wie Sie diese abhängig von den in einem Formular angezeigten... [mehr]

Benutzerverwaltung mit verschlüsselten...

Wenn Sie in einer Access-Anwendung Benutzer verwalten wollen, die sich per Benutzername und Kennwort an die Anwendung anmelden, sollten Sie sehr sensibel mit den in der Anwendung gespeicherten... [mehr]

HTML-Tabellen mit fester Kopfzeile

In den vorherigen Ausgaben von Access im Unternehmen und in der aktuellen Ausgabe arbeiten wir in einigen Beiträgen mit dem Webbrowser-Steuerelement und stellen Daten, die wir mit den Bordmitteln... [mehr]

Flexible HTML-Tabellen mit fester Kopfzeile

Im Beitrag »HTML-Tabellen mit fester Kopfzeile« haben wir gezeigt, wie Sie Daten aus einer bestimmten Abfrage in einem Webbrowser-Steuerelement so anzeigen, dass die Spaltenköpfe oben fixiert... [mehr]

Berechtigungen per HTML verwalten

Im Beitrag »Benutzerverwaltung mit verschlüsselten Kennwörtern« stellen wir eine Lösung vor, in der wir die Berechtigungen von Benutzergruppen an Datenbankobjekten definieren. Dort benötigen wir... [mehr]

Benutzer und Berechtigungen ermitteln

In den Beiträgen »Benutzerverwaltung mit verschlüsselten Kennwörtern« und »Berechtigungen per HTML verwalten« haben wir die Voraussetzungen für eine Benutzerverwaltung geschaffen. Im vorliegenden... [mehr]

Zugriffsrechte mit Datenmakros

Es gibt verschiedene Möglichkeiten, auf Basis des aktuell angemeldeten Benutzers sicherzustellen, dass dieser nur die für ihn vorgesehenen Aktionen mit Daten durchführen darf – beispielsweise durch... [mehr]

Kennwörter generieren

Für den einen oder anderen Zweck möchten Sie vielleicht Kennwörter generieren oder in einer Benutzeroberfläche die Möglichkeit zum Generieren von Kennwörtern anbieten. Wenn Sie etwa Benutzer zu... [mehr]

Neuer Datensatz von Frontend zu Backend

Für manche Themen gibt es keine kurze, prägnante Überschrift. In diesem Fall wollen wir zeigen, wie Sie einen neuen Datensatz anlegen, der in einer temporären Tabelle im Frontend gespeichert wird,... [mehr]