Lies in den Artikel rein und unten bekommst Du ein unschlagbares Angebot!
Im Artikel “Access zu WPF: Detailformulare mit Textfeldern” schauen wir uns an, wie die programmgesteuerten Möglichkeiten aussehen, um Formulare automatisch als WPF-Fenster oder -Seiten abzubilden. Damit haben wir einfache Detailformulare samt Textfeldern und Datenbindung unter WPF abgebildet. Nun wollen wir einen Schritt weitergehen und uns um weitere Steuer-elemente wie etwa Kombinationsfelder und Kontrollkästchen kümmern. Außerdem wollen wir noch Schaltflächen zum Blättern in den Datensätzen sowie zum Anlegen neuer Datensätze hinzufügen.
Voraussetzungen
Wir gehen an dieser Stelle davon aus, dass Sie bereits ein Entity Data Model auf Basis des Access-Datenmodells erstellt und eine entsprechende SQL Server-Datenbank auf Basis des Entity Data Modells erstellt haben. Wie das gelingt, zeigen die Artikel Von Access zu Entity Framework: Datenmodell und Von Access zu Entity Framework: Daten aus Ausgabe 5/2018. Die weiteren Vorarbeiten werden im Artikel Access zu WPF: Detailformulare mit Textfeldern erläutert.
Kombinationsfelder hinzufügen
Wenn wir etwa das Kombinationsfeld cboAnredeID im WPF-Fenster abbilden möchten, benötigen wir einige weitere Elemente. Das erste ist natürlich das ComboBox-Element im XAML-Code. Diesem müssen wir die Eigenschaften für die Bindung übergeben. Außerdem brauchen wir im Code behind-Modul der WPF-Seite zusätzlichen Code, der die Auflistung der Anreden als öffentliche Eigenschaft bereithält, damit wir vom XAML-Code aus auf die Anreden zugreifen können. Wir schauen uns als Erstes die notwendigen Änderungen an. Im XAML-Code fügen wir allgemeine Eigenschaften für den Steuerelementtyp ComboBox hinzu:
<Window x:Class="frmKundendetails" ... Title="frmKundendetails" Height="332" Width="362"> <Window.Resources> ... <Style TargetType="{x:Type ComboBox}"> <Setter Property="HorizontalAlignment" Value="Left"></Setter> <Setter Property="VerticalAlignment" Value="Top"></Setter> <Setter Property="FontFamily" Value="Calibri"></Setter> <Setter Property="FontSize" Value="11pt"></Setter> </Style> </Window.Resources>
Für das Kombinationsfeld selbst fügen wir das ComboBox-Element hinzu. Im Beispielformular, das die Daten eines Kunden anzeigen soll, wollen wir mit der folgenden Definition eines ComboBox-Elements die Daten der Tabelle Anreden zur Auswahl einfügen und die für den aktuellen Kunden ausgewählte Anrede anzeigen:
<Grid> ... <Label x:Name="Bezeichnungsfeld4" Content="Anrede:" Padding="0" Height="21" Margin="5,111,0,0" Width="64"/> <ComboBox x:Name="MehrwertsteuersatzID" Padding="0" Height="21" Margin="118,111,0,0" Width="221" ItemsSource="{Binding Anreden}" SelectedItem="{Binding Kunde.Anrede, ValidatesOnDataErrors=True}" SelectedValuePath="ID" SelectionChanged="Anrede_SelectionChanged"> <ComboBox.ItemTemplate> <DataTemplate> <TextBlock> <TextBlock.Text> <MultiBinding StringFormat="{}{0}"> <!--<MultiBinding StringFormat="{}{0}, {1}">--> <Binding Path="Name" /> <!--<Binding Path="Vorname" />--> </MultiBinding> </TextBlock.Text> </TextBlock> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> .... </Grid> </Window>
In der Code behind-Klasse fügen wir zunächst im allgemeinen Teil die private Variable für die Liste der Anreden hinzu sowie die öffentliche Eigenschaft, über welche diese dann verfügbar ist:
Private _Anreden As List(Of Anrede) Public Property Anreden As List(Of Anrede) Get Return _Anreden End Get Set(value As List(Of Anrede)) _Anreden = value End Set End Property
Schließlich benötigen wir in den Konstruktor-Methoden jeweils eine Anweisung, welche die Auflistung der Anreden mit den Daten der DbSet-Auflistung Anreden füllt:
Public Sub New() ... Anreden = New List(Of Anrede)(dbContext.Anreden) DataContext = Me End Sub Public Sub New(lngID As Long) ... Anreden = New List(Of Anrede)(dbContext.Anreden) DataContext = Me End Sub
Standardeigenschaften für ComboBox-Element hinzufügen
Das wären zunächst einmal die Anforderungen für die einzufügenden Elemente, die wir in den Prozeduren des Moduls mdlAccessZuWPF berücksichtigen müssen. Die Erweiterungen sind teilweise trivial wie etwa die für die Standardeigenschaften für die Kombinationsfelder beziehungsweise ComboBox-Elemente. Diese fügen wir der Prozedur AttributeHinzufuegen wie folgt hinzu:
Public Sub AttributeHinzufuegen(frm As Form, strXAML As String) strXAML = strXAML & " <Window.Resources>" & vbCrLf ... With frm.DefaultControl(acComboBox) strXAML = strXAML & " <Style TargetType=""{x:Type ComboBox}"">" & vbCrLf strXAML = strXAML & " <Setter Property=""HorizontalAlignment"" Value=""Left""></Setter>" & vbCrLf strXAML = strXAML & " <Setter Property=""VerticalAlignment"" Value=""Top""></Setter>" & vbCrLf strXAML = strXAML & " <Setter Property=""FontFamily"" Value=""" & .FontName & """></Setter>" & vbCrLf strXAML = strXAML & " <Setter Property=""FontSize"" Value=""" & .FontSize & "pt""></Setter>" & vbCrLf strXAML = strXAML & " </Style>" & vbCrLf End With strXAML = strXAML & " </Window.Resources>" & vbCrLf End Sub
ComboBox-Element hinzufügen
Der Teil, der die XAML-Definition des ComboBox-Elements zusammenstellt, ist etwas umfangreicher als der für das Zusammenstellen eines Label– oder eines TextBox-Elements. Das liegt daran, dass wir ein paar mehr Informationen sammeln müssen – etwa, um herauszufinden, welche Tabelle beziehungsweise welches DbSet-Element die Daten für das aufgeklappte ComboBox-Element liefert. Genau genommen ist es sogar recht kompliziert – und umso komplizierter, je mehr mögliche Fälle man abdecken möchte.
Gehen wir einmal davon aus, dass wir etwa das Kombinationsfeld zur Auswahl der Anrede eines Kunden abbilden wollen. Dann hat dieses Kombinationsfeld beispielsweise die folgende Abfrage als Datensatzherkunft:
SELECT [tblAnreden].[AnredeID], [tblAnreden].[Anrede] FROM tblAnreden;
Daneben verwendet es die erste Spalte dieser Abfrage als gebundene Spalte und die zweite soll angezeigt werden. Schließlich ist das Kombinationsfeld an das Feld AnredeID der Tabelle tblKunden gebunden. Diese Informationen benötigen wir auch für das ComboBox-Element – genau genommen sogar einige davon abgeleitete Informationen. Wir wollen ja nicht den Namen der Tabelle tblAnreden für das Attribut ItemsSource angeben, sondern benötigen das DbSet des Entity Data Models, dass die Daten dieser Tabelle liefert.
Generiertes Entity Data Model nutzen
In den Artikeln Von Access zu Entity Framework: Datenmodell und Von Access zu Entity Framework: Update 1 haben wir gezeigt, wie Sie aus dem Datenmodell einer Access-Datenbank ein Entity Data Model erstellen können. Dort haben wir unter anderem eine Collection zusammengestellt, welche die resultierenden Mappings enthält – also etwa die Namen der Tabellen (zum Beispiel tblKunden), der resultierenden Klasse (Kunde), des DbSet-Elements (Kunden) und weiterer Informationen. Die Daten aus dieser Collection können wir auch noch nutzen, um die Informationen zur Definition der ComboBox-Elemente zusammenzustellen. In der Collection, die Elemente des Typs clsMapping aufnimmt, fehlen allerdings noch zwei Informationen, die wir noch benötigen: Wenn der Name eines Feldes mit dem resultierenden Namen der Klasse übereinstimmt, was etwa bei der Tabelle tblAnreden mit den Feldern AnredeID und Anrede auftritt, dann soll der Name dieses Feldes als Eigenschaft der Klasse in Name umbenannt werden. Da wir zufällig genau beim Beispiel der Tabelle tblKunden, welche ja im Fremdschlüsselfeld AnredeID auf die Datensätze der Tabelle tblAnreden zugreift, auf diesen Fall treffen, wissen wir auch, dass diese Informationen gut noch in den Elementen der Collection colMappings untergebracht werden könnten. Dazu erweitern wir zunächst die Klasse clsMapping in der Access-Datenbank, welche die zu migrierenden Formulare enthält, um die folgenden beiden privaten Variablen und die entsprechenden Getter und Setter:
Private m_RenamedField As String Private m_RenamedFieldOriginal As String Public Property Get RenamedField() As String RenamedField = m_RenamedField End Property Public Property Let RenamedField(str As String) m_RenamedField = str End Property Public Property Get RenamedFieldOriginal() As String RenamedFieldOriginal = m_RenamedFieldOriginal End Property Public Property Let RenamedFieldOriginal(str As String) m_RenamedFieldOriginal = str End Property
Die Eigenschaft RenamedFieldOriginal soll dann etwa den Namen des Feldes Anrede aufnehmen und RenamedField den Namen der resultierenden Eigenschaft der Klasse Anrede, also Name.
Die Prozedur EDMErstellen aus dem Modul mdlEDM wandeln wir in eine gleichnamige Funktion um, welche eine Collection als Funktionswert zurückliefern soll:
Public Function EDMErstellen() As Collection ... Set colMappings = New Collection ...
In der For Each-Schleife über alle Mappings, in denen die Funktion die Felder untersucht, schreibt die Funktion dann die entsprechenden Werte in die beiden Eigenschften RenamedField und RenamedFieldOriginal der clsMapping-Klasse:
For Each objMapping In colMappings With objMapping ... For Each fld In tdf.Fields ... If fld.Name = .Entity_Original Then strFieldname = "Name" objMapping.RenamedField = "Name" objMapping.RenamedFieldOriginal = fld.Name Else ... End If ... Next fld ... End With ... Next objMapping ...
Schließlich wird die Collection colMappings als Funktionswert der Funktion EDMErstellen zurückgegeben:
Set EDMErstellen = colMappings End Function
Anpassung der Prozeduren zum Migrieren des Formulars