Access zu WPF: Detailformulare mit Textfeldern

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.

Das Formular frmKunden in der Formularansicht

Bild 1: Das Formular frmKunden in der Formularansicht

Projekt 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 Steuer­elemente auslesen und im WPF-Fenster anlegen. Diese sollen, genau wie das Fenster, mit den Eigenschaften zum Binden des Fensters und der Steuer­elemente an die Daten versehen werden. Schließlich fehlen noch die Ereignisprozeduren, die durch die Steuer­elemente 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 Steuer­elementeHinzufuegen, 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.

Höhe, Breite, Titel und Name wurden vom Access-Formular übernommen

Bild 2: Höhe, Breite, Titel und Name wurden vom Access-Formular übernommen

Ob 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

Steuer­elemente 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 Steuer­elemente des Formulars über die Controls-Auflistung und referenzieren das aktuelle Steuer­element 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 Steuer­element 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.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.