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.
Access zu WPF: Validierung und Navigation
Im Artikel »Access zu WPF: Detailformulare mit Combo, Checkbox und Button« haben wir gezeigt, wie Sie die in Formularen enthaltenen gebundenen Steuerelemente wie Kombinationsfelder und Kontrollkästchen sowie Schaltflächen in WPF-Fenster übertragen. In diesem Artikel wollen wir uns ansehen, wie Sie einem WPF-Fenster auf Basis eines Access-Detailformulars eine Validierung und Navigationsschaltflächen hinzufügen. Die notwendigen Techniken haben wir bereits im Artikel »EDM: Blättern in Datensätzen« vorgestellt, nun integrieren wir diese in unsere VBA-Prozedur zum Erstellen des Codes für ein WPF-Fenster mit Navigationsschaltflächen.
Um festzulegen, ob ein Formular mit oder ohne Validierung und Navigationsschaltflächen erstellt werden soll, fügen wir der Prozedur FormularNachWPF zwei weitere Parameter hinzu:
- bolValidation: Gibt an, ob die Validierung hinzugefügt werden soll.
- bolNavigation: Gibt an, ob die Navigationsschaltflächen hinzugefügt werden sollen.
Die Validierung für einen neuen, leeren Datensatz sieht dann etwa wie in Bild 1 aus.
Bild 1: Validierung bei einem neuen, leeren Datensatz
Die Prozedur FormularNachWPF erweitern wir an den folgenden Stellen um die Entgegennahme beziehungsweise Übergabe der Parameter:
Public Sub FormularNachWPF(strForm As String, strTabelle As String, Optional bolValidation As Boolean, _
Optional bolNavigation As Boolean)
...
lngHeight = TwipsToPixelsHeight(frm.Section(0).Height) + 30
If bolNavigation = True Then
lngTopNavi = lngHeight - 30
lngHeight = lngHeight + 28
End If
Bei der Definition des Window-Elements fügen wir noch das Ereignisattribut Closing mit dem Wert Window_Closing hinzu, welches sicherstellt, dass das Fenster nur bei geändertem und gespeichertem Datensatz geschlossen werden kann:
strXAML = strXAML & "<Window x:Class=""" & frm.Name & """" & vbCrLf
...
strXAML = strXAML & " Title=""" & strTitle & """ Height=""" & lngHeight & """ Width=""" & lngWidth _
& """ Closing=""Window_Closing"">" & vbCrLf
Für die folgenden Aufruf haben wir die Übergabe der Parameter bolValidation und bolNavigation erweitert:
...
AttributeHinzufuegen frm, strXAML, bolValidation, bolNavigation
...
SteuerelementeHinzufuegen frm, strXAML, strTabelle, colMappings, bolValidation, bolNavigation
If bolNavigation Then
NavigationHinzufuegen strXAML, lngTopNavi
End If
...
End Sub
Erweiterung der Prozedur AttributeHinzufuegen
Die XAML-Datei enthält mit eingebauter Validierung einige Zeilen Code mehr. Diesem werden wir in der Prozedur AttributeHinzufuegen wie folgt gerecht, die ebenfalls die beiden Parameter bolValidation und bolNavigation erwartet:
Public Sub AttributeHinzufuegen(frm As Form, strXAML As String, bolValidation As Boolean, bolNavigation As Boolean)
In dieser Prozedur geht es allein um das Zusammenstellen allgemeiner Properties für Steuerelemente im WindowResources-Bereich. Da wir hier auch die Steuerelemente durchlaufen, um die DataTrigger und MultiDataTrigger für die Validierung zu definieren, benötigen wir eine Variable namens ctl mit dem Typ Control:
Dim ctl As Control
Danach geht es dann direkt rund. Wir prüfen, ob bolValidation den Wert True aufweist und fügen dann die Vorlage für die Markierung nicht erfolgreich validierter Steuerelemente hinzu:
strXAML = strXAML & " <Window.Resources>" & vbCrLf
If bolValidation Then
strXAML = strXAML & " <Style TargetType=""{x:Type FrameworkElement}"">" & vbCrLf
strXAML = strXAML & " <Setter Property=""Validation.ErrorTemplate"">" & vbCrLf
strXAML = strXAML & " <Setter.Value>" & vbCrLf
strXAML = strXAML & " <ControlTemplate>" & vbCrLf
strXAML = strXAML & " <DockPanel>" & vbCrLf
strXAML = strXAML & " <Border Background=""Red"" DockPanel.Dock=""right"" " _
& "Margin=""5,0,0,0"" Width=""20"" Height=""20"" CornerRadius=""10""" & vbCrLf
strXAML = strXAML & " ToolTip=""{Binding ElementName=customAdorner, " _
& "Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"">" & vbCrLf
strXAML = strXAML & " <TextBlock Text=""!"" VerticalAlignment=""center"" " _
& "HorizontalAlignment=""center""" & vbCrLf
strXAML = strXAML & " FontWeight=""Bold"" Foreground=""white"" />" & vbCrLf
strXAML = strXAML & " </Border>" & vbCrLf
strXAML = strXAML & " <AdornedElementPlaceholder Name=""customAdorner"" " _
& "VerticalAlignment=""Center"" >" & vbCrLf
strXAML = strXAML & " <Border BorderBrush=""red"" BorderThickness=""1"" />" & vbCrLf
strXAML = strXAML & " </AdornedElementPlaceholder>" & vbCrLf
strXAML = strXAML & " </DockPanel>" & vbCrLf
strXAML = strXAML & " </ControlTemplate>" & vbCrLf
strXAML = strXAML & " </Setter.Value>" & vbCrLf
strXAML = strXAML & " </Setter>" & vbCrLf
strXAML = strXAML & " </Style>" & vbCrLf
End If
...
Der nächste Teil, der nur von der Notwendigkeit einer Validierung abhängt, sind die Elemente ComboBox und TextBox, die wir beide mit den Style-Attributen für die Validierung ausstatten müssen – also mit dem soeben angelegten Style. Dazu prüfen wir wieder per If...Then-Bedingung, ob bolValidation den Wert True aufweist, und fügen dann eine der beiden Zeilen als erste Zeile des Styles hinzu:
...
With frm.DefaultControl(acTextBox)
If bolValidation Then
strXAML = strXAML & " <Style TargetType=""{x:Type TextBox}"" "_
& "BasedOn=""{StaticResource {x:Type FrameworkElement}}"">" & vbCrLf
Else
strXAML = strXAML & " <Style TargetType=""{x:Type TextBox}"">" & vbCrLf
End If
...
End With
With frm.DefaultControl(acComboBox)
If bolValidation Then
strXAML = strXAML & " <Style TargetType=""{x:Type ComboBox}"" " _
& "BasedOn=""{StaticResource {x:Type FrameworkElement}}"">" & vbCrLf
Else
strXAML = strXAML & " <Style TargetType=""{x:Type ComboBox}"">" & vbCrLf
End If
...
End With
...
Mit dem BasedOn-Attribut sorgen wir dafür, dass der oben definierte, für den Typ FrameworkElement vorgesehene Style sowohl an ComboBox- als auch TextBox-Elemente vererbt wird. Durch das Erfassen der Attribute für die Markierung nicht erfolgreich validierter Steuerelemente in einem eigenen Style-Element sparen wir einige Zeilen Code, da wir die gleichen Elemente nicht für beide Steuerelementtypen einzeln hinterlegen müssen.
Erst wollten wir auch das CheckBox-Element auf diese Weise ausstatten, aber dieses ist ja ohnehin meist mit dem Wert False vorbelegt, sodass keine Validierung notwendig ist.
Danach stellen wir die DataTrigger-Elemente für drei Steuerelementtypen ComboBox, TextBox und CheckBox zusammen, die dafür sorgen, dass die Eigenschaft IsEnabled, die wir später im Code behind-Modul definieren, für die mit diesem DataTrigger ausgestatteten Style auf False eingestellt wird, wenn die Validierung auch nur für ein einziges der Steuerelemente fehlschlägt. Dies betrifft etwa die Schaltflächen zum Navigieren, die wir später hinzufügen. Hier verwenden wir zum ersten Mal die Variable ctl, mit der wir in einer For Each-Schleife alle Steuerelemente des Formulars durchlaufen. Damit fügen wir dem Style-Element, das wir über das Attribut x:Key mit dem Wert EnableOnValidation identifizieren, für jedes Steuerelement drei Zeilen hinzu, die das DataTrigger-Element mit der Bedingung und das Setter-Element mit dem zu setzenden Wert enthalten:
...
If bolValidation Then
strXAML = strXAML & " <Style x:Key=""EnableOnValidation"" TargetType=""{x:Type Button}"">" & vbCrLf
strXAML = strXAML & " <Style.Triggers>" & vbCrLf
For Each ctl In frm.Controls
Select Case ctl.ControlType
Case acComboBox, acCheckBox, acTextBox
If Not Len(ctl.ControlSource) = 0 Then
strXAML = strXAML & " <DataTrigger Binding=""{Binding ElementName=" & ctl.Name _
& ", Path=(Validation.HasError)}"" Value=""True"">" & vbCrLf
strXAML = strXAML & " <Setter Property=""IsEnabled"" " _
& "Value=""False""></Setter>" & vbCrLf
strXAML = strXAML & " </DataTrigger>" & vbCrLf
End If
End Select
Next ctl
strXAML = strXAML & " </Style.Triggers>" & vbCrLf
strXAML = strXAML & " </Style>" & vbCrLf
...
Außerdem benötigen wir noch jeweils ein MultiDataTrigger-Element für jedes gebundene Steuerelement der Typen ComboBox, TextBox und CheckBox. Dieses liefert standardmäßig für das Attribut IsEnabled den Wert True. Wenn alle in den Condition-Elementen genannten Bedingungen wahr sind, also die Validierung für alle Elemente keinen Fehler liefert, wird IsEnabled auf False eingestellt. Dieser mit dem Schlüssel EnableOnFailedValidation benannte Style wird für die Rückgängig-Schaltfläche eingesetzt, um diese nur zu aktivieren, wenn mindestens eine Validierung fehlschlägt und diese nur zu deaktivieren, wenn alle Validierungen erfolgreich waren. Auch hier durchlaufen wir in einer For Each-Schleife alle gebundenen Steuerelemente:
...
strXAML = strXAML & " <Style x:Key=""EnableOnFailedValidation"" TargetType=""{x:Type Button}"">" & vbCrLf
strXAML = strXAML & " <Setter Property=""IsEnabled"" Value=""True"" />" & vbCrLf
strXAML = strXAML & " <Style.Triggers>" & vbCrLf
strXAML = strXAML & " <MultiDataTrigger>" & vbCrLf
strXAML = strXAML & " <MultiDataTrigger.Conditions>" & vbCrLf
For Each ctl In frm.Controls
Select Case ctl.ControlType
Case acComboBox, acCheckBox, acTextBox
If Not Len(ctl.ControlSource) = 0 Then
strXAML = strXAML & " <Condition Binding=""{Binding ElementName=" _
& ctl.Name & ", Path=(Validation.HasError)}"" Value=""False"" />" & vbCrLf
End If
End Select
Next ctl
strXAML = strXAML & " </MultiDataTrigger.Conditions>" & vbCrLf
strXAML = strXAML & " <Setter Property=""IsEnabled"" Value=""False"" />" & vbCrLf
strXAML = strXAML & " </MultiDataTrigger>" & vbCrLf
Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.