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: Detailformulare mit Textfeldern
Wir haben uns bereits in einigen Artikel angesehen, wie Sie Übersichtsformulare, Detailformulare und so weiter unter WPF anlegen. Was aber, wenn Sie keine Lust haben, die Formulare unter WPF alle neu zu programmieren, obwohl Sie das schon unter Access erledigt haben? In diesem Artikel schauen wir uns an, wie die programmgesteuerten Möglichkeiten aussehen, um Formulare automatisch als WPF-Fenster oder -Seiten abzubilden. Es wird ein wenig Handarbeit übrig bleiben, aber einen großen Teil der Schritte können Sie sich damit deutlich erleichtern.
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.
Nun wollen wir versuchen, die Migration einfacher Formulare von Access aus in funktionsfähige WPF-Fenster zu erreichen.
Beispiel: Kundenformular
Unser Beispiel finden Sie in der Beispieldatenbank Bestellverwaltung.accdb im Download zu diesem Artikel. Wir wollen das Formular frmKundendetails als WPF-Fenster nachbauen. Das Formular sehen Sie in Bild 1 in der Entwurfsansicht. Es verwendet die Tabelle tblKunden als Datensatzquelle. Die Textfelder sind jeweils an entsprechende Felder der Datensatzquelle des Formulars gebunden. Das Kombinationsfeld cboAnredeID verwendet die Abfrage SELECT [tblAnreden].[AnredeID], [tblAnreden].[Anrede] FROM tblAnreden; als Datensatzherkunft und ist an das Feld AnredeID gebunden. Für das erste Feld der Datensatzherkunft haben wir die Spaltenbreite auf 0cm eingestellt, damit dieses nicht angezeigt wird. Es erscheint also nur das zweite Feld der Datensatzherkunft.
Bild 1: Das Formular frmKunden in der FormularansichtProjekt erstellen
Wir erstellen ein neues Projekt namens AccessZuWPFFormulare und führen die im Artikel Von Access zu EF: Step by step aus Ausgabe 5/2018 beschriebenen Schritte aus, um die Tabellenentwürfe und Daten der Tabellen unserer Beispieldatenbank als Entity Data Model hinzuzufügen. Das gelingt innerhalb von ungefähr fünf Minuten, wenn Sie die Beispieldatenbank dazu verwenden. Für andere Datenbanken haben wir dies noch nicht getestet. Wenn Sie versuchen, andere Datenbanken zu migrieren und auf Fehler stoßen, teilen Sie uns das gern mit. Wir benötigen dann allerdings eine Kopie des Datenmodells im Access-Format, damit wir damit experimentieren und unsere Tools verbessern können (per E-Mail an andre@minhorst.com).
Zum Testen der migrierten Datenbank verwenden Sie eine neue Schaltfläche auf der Seite MainWindow.xaml, welche die folgende Methode aufruft:
Private Sub btnTestDatabase_Click(sender As Object, e As RoutedEventArgs)
Dim dbContext As BestellverwaltungContext
Dim Kunden As ObservableCollection(Of Kunde)
dbContext = New BestellverwaltungContext
Kunden = New ObservableCollection(Of Kunde)(dbContext.Kunden)
MessageBox.Show("Anzahl Kunden: " + Kunden.Count().ToString())
End Sub
Danach sollte beim Starten des Projekts und Anklicken der Schaltfläche ein Meldungsfenster mit der Anzahl der Datensätze der Tabelle Kunden erscheinen.
Vom Formular zu WPF
Nun schauen wir uns an, was für eine Aufgabe überhaupt vor uns liegt. Wir wollen das Formular frmKundendetails aus der Access-Datenbank als Fenster der WPF-Anwendung abbilden. Was benötigen wir dazu? Zunächst einmal ist soll das Fenster die Größe erhalten, die auch unser Access-Formular aufweist. Dann wollen wir die Steuerelemente auslesen und im WPF-Fenster anlegen. Diese sollen, genau wie das Fenster, mit den Eigenschaften zum Binden des Fensters und der Steuerelemente an die Daten versehen werden. Schließlich fehlen noch die Ereignisprozeduren, die durch die Steuerelemente und das Formular selbst ausgelöst werden.
Wir wollen zunächst von Access aus starten und hier Code schreiben, mit dem wir die notwendigen Informationen aus dem Formularentwurf auslesen und daraus automatisiert die XAML-Definition für das WPF-Fenster sowie den Inhalt der Code behind-Daten für das Fenster erstellen. Diesen kopieren wir dann zunächst von Hand in ein neues Fenster-Element in unserem Projekt.
Fenstergröße
Wir beginnen einmal mit einer vermeintlich einfachen Aufgabe: dem Ermitteln der Fenstergröße und dem Erstellen des Code-Elements, welches diese beiden Eigenschaften enthält. Wenn wir die Eigenschaften im Eigenschaftenblatt von Access ansehen, erhalten wir Angaben in der Einheit Zentimeter, also beispielsweise 9,198cm für die Formularbreite und 9,198cm für die Höhe des Detailbereichs. Geben wir die Eigenschaften Width des Form-Elements und Me.Height des Detailbereichs (Me.Section(0).Height) aus, erhalten wir Werte wie 4535 und 5215. Wenn wir die Eigenschaften in der XAML-Definition betrachten, finden wir Werte wie 450 für die Eigenschaft Height und 800 für die Eigenschaft Width. Wir müssen die Zahlen also noch umrechnen. Die Größe und Breite unter Visual Studio wird in Pixel angegeben. Unter Access werden diese Werte in Twips angegeben. Zur Umrechnung von Twips in Pixel gibt es ausreichend Funktionen, von denen wir zwei im Modul mdlUmrechnungen der Beispieldatenbank eingefügt haben. Sie heißen TwipsToPixelHeight und TwipsToPixelWidth. Wir benötigen zwei Funktionen, weil die Faktoren für die Umrechnungen unterschiedlich sein können.
Basisprozedur
Damit legen wir nun die Basis für das Erstellen des WPF-Codes. Die benötigte VBA-Prozedur sieht wie folgt aus, wobei wir zunächst einige Variablen deklarieren:
Public Sub FormularNachWPF()
Dim frm As Form
Dim strForm As String
Dim strXAML As String
Dim lngHeight As Long
Dim lngWidth As Long
Dim strTitle As String
Dann öffnen wir das Formular in der Entwurfsansicht und weisen dieses der Variablen frm zu:
strForm = "frmKundendetails"
DoCmd.OpenForm strForm, acDesign
Set frm = Forms(strForm)
Dann ermitteln wir mit den Umrechnungsfunktionen die Höhe und Breite:
lngHeight = TwipsToPixelsHeight(frm.Section(0).Height)
lngWidth = TwipsToPixelswidth(frm.Width)
Außerdem prüfen wir, ob ein Titel für das Formular angegeben wurde und schreiben diesen oder alternativ den Formularnamen in die Variable strTitle:
If Not Len(frm.Caption) = 0 Then
strTitle = frm.Caption
Else
strTitle = frm.Name
End If
Schließlich bauen wir den grundlegenden XAML-Code für das Fenster zusammen, wobei wir den standardmäßig verwendeten Code nutzen und unsere ermittelten Werte für Fenstername, Titel, Höhe und Breite an den entsprechenden Stellen einfügen:
strXAML = strXAML & "<Window x:Class=""" & frm.Name & """" & vbCrLf
strXAML = strXAML & " xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""" & vbCrLf
strXAML = strXAML & " xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""" & vbCrLf
strXAML = strXAML & " xmlns:d=""http://schemas.microsoft.com/expression/blend/2008""" & vbCrLf
strXAML = strXAML & " xmlns:mc=""http://schemas.openxmlformats.org/markup-compatibility/2006""" & vbCrLf
strXAML = strXAML & " xmlns:local=""clr-namespace:AccessZuWPFFormulare""" & vbCrLf
strXAML = strXAML & " mc:Ignorable=""d""" & vbCrLf
strXAML = strXAML & " Title=""" & strTitle & """ Height=""" & lngHeight & """ Width=""" & lngWidth & """>" & vbCrLf
strXAML = strXAML & " <Grid>" & vbCrLf
Dazwischen platzieren wir den Aufruf der Prozedur SteuerelementeHinzufuegen, die wir im Anschluss erstellen – für den ersten Testlauf bleibt diese auskommentiert:
'SteuerelementeHinzufuegen frm, strXAML
strXAML = strXAML & " </Grid>" & vbCrLf
strXAML = strXAML & "</Window>" & vbCrLf
Schließlich nutzen wir die Prozedur InZwischenablage, um den Inhalt der String-Variablen strXAML in die Zwischenablage zu kopieren (siehe mdlZwischenablage) und schließen das Formular wieder:
Inzwischenablage strXAML
DoCmd.Close acForm, strForm
End Sub
Wenn Sie diese Prozedur ausführen und den Inhalt der Zwischenablage als XAML-Code eines neuen Fensters einsetzen, erhalten Sie die Ansicht aus Bild 2.
Bild 2: Höhe, Breite, Titel und Name wurden vom Access-Formular übernommenOb dieses Fenster unseren Ansprüchen genügt, müssen wir allerdings noch herausfinden – dazu benötigen wir im Fenster MainWindows.xaml noch eine Schaltfläche, mit der wir das neu erstellte Fenster öffnen können. Diese soll den folgenden Code ausführen:
Private Sub btnKundendetails_Click(sender As Object, e As RoutedEventArgs)
Dim Kundendetails As frmKundendetails
Kundendetails = New frmKundendetails()
Kundendetails.Show()
End Sub
Steuerelemente migrieren
Als nächstes wollen wir uns um die Steuerelemente kümmern. Den folgenden Aufruf haben wir ja schon im Code platziert – Sie müssen nur noch die Markierung als Kommentar entfernen:
SteuerelementeHinzufuegen frm, strXAML
Die Prozedur selbst erwartet die beiden übergebenen Parameter, nämlich einen Verweis auf das Formular (frm) und die Variable zum Zusammenstellen des XAML-Codes (strXAML). Damit starten wir in die erste Rohfassung der Prozedur:
Public Function SteuerelementeHinzufuegen(frm As Form, strXAML As String)
Dim ctl As Control
Dim lngWidth As Long
Dim lngHeight As Long
Dim lngTop As Long
Dim lngLeft As Long
Dim strCaption As String
In dieser durchlaufen wir alle Steuerelemente des Formulars über die Controls-Auflistung und referenzieren das aktuelle Steuerelement jeweils mit der Variablen ctl. Hier tragen wir zunächst die Größe und die Position in die entsprechenden Variablen ein:
For Each ctl In frm.Controls
lngWidth = TwipsToPixelsWidth(ctl.Width)
lngHeight = TwipsToPixelsHeight(ctl.Height)
lngTop = TwipsToPixelsHeight(ctl.Top)
lngLeft = TwipsToPixelsWidth(ctl.Left)
Dann prüfen wir in einer Select Case-Schleife, welchen Typ das aktuell mit ctl referenzierte Steuerelement hat. In diesem Fall haben wir zunächst die Typen acTextBox und acLabel untersucht und fügen für jedes Bezeichnungsfeld und jedes Textfeld eine Zeile zu strXAML hinzu, welche das Aussehen des jeweiligen Steuerelements beschreibt:
Select Case ctl.ControlType
Case acTextBox
strXAML = strXAML & "<TextBox x:Name=""" & ctl.Name & """ FontFamily=""Calibri"" FontSize=""" _
& ctl.FontSize & "pt"" HorizontalAlignment=""Left"" Height=""" & lngHeight & """ Margin=""" _
& lngLeft & "," & lngTop & ",0,0"" TextWrapping=""Wrap"" Text=""TextBox"" " _
& "VerticalAlignment=""Top"" Width=""" & lngWidth & """/>" & vbCrLf
Case acLabel
strCaption = ctl.Caption
strXAML = strXAML & "<Label x:Name=""" & ctl.Name & """ Content=""" & strCaption _
& """ FontFamily=""Calibri"" FontSize=""11pt"" Padding=""0"" " _
& "VerticalContentAlignment=""Center"" HorizontalAlignment=""Left"" Height=""" & lngHeight _
& """ Margin=""" & lngLeft & "," & lngTop & ",0,0"" VerticalAlignment=""Top"" Width=""" & lngWidth _
& """/>" & vbCrLf
Case acCommandButton
Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.