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.
Entity Framework: Bilder in WPF
Im Artikel »PowerApps: Bilder in Datenbank speichern« haben wir gezeigt, wie Sie mit einer PowerApp über ein Smartphone oder ein Tablet Bilder aufnehmen und diese dann in einer Datenbank speichern. Nun wollen wir uns ansehen, wie Sie diese Bilder aus der Datenbank in einer WPF-Anwendung anzeigen, die über das Entity Framework auf die Tabellen der Datenbank zugreift.
Voraussetzung
Um die Beispiele dieses Artikels nachzuvollziehen, benötigen Sie eine Datenbank, die eine Tabelle namens Fotos enthält – genau wie die, die wir im oben genannten Artikel erstellt haben. Diese enthält ein Primärschlüsselfeld namens ID und mit dem Datentyp int sowie ein Feld namens Foto und mit dem Datentyp image. Unsere Beispieldatenbank liegt auf einem Azure-Server. Sie können aber natürlich auch unsere Beispieldatenbank mit ein paar Beispielfotos aus dem SQL-Skript aus dem Download auf einem lokalen SQL Server anlegen und mit der folgenden Anwendung auf diese Datenbank zugreifen.
Vorbereitung
Wir erstellen in Visual Studio ein neues Projekt auf Basis der Vorlage Visual Basic|Windows Desktop|WPF-App. Da wir diesmal nicht, wie so oft in den vorherigen Ausgaben, ein Code First-Model erstellen, sondern mit einer bestehenden Datenbank starten, noch einmal eine kurze Beschreibung der nötigen Schritte. Dem neuen Projekt fügen wir als Erstes ein neues Element hinzu und wählen den Typ ADO.NET Entity Data Model aus. Nachdem wir als Name FotoverwaltungContext eingestellt haben, klicken wir auf Hinzufügen (siehe Bild 1).
Bild 1: Einfügen des Entity Data ModelsDanach wählen wir als Modellinhalt Code First aus Datenbank aus (siehe Bild 2). Der folgende Dialog bietet die Möglichkeit zur Auswahl der Datenverbindung. Hier klicken wir auf die Schaltfläche Neue Verbindung. Im nun erscheinenden Dialog namens Verbindungseigenschaften geben Sie unter Servername den Namen des gewünschten Servers ein. Danach wählen Sie die Authentifizierungsmethode aus – beim Zugriff auf eine Azure-Datenbank immer die SQL Server-Authentifizierung – und geben die Zugangsdaten ein. Schließlich wählen Sie die Datenbank aus und bestätigen nach einem erfolgreichen Test mit einem Klick auf die Schaltfläche Testverbindung die Eingaben mit OK (siehe Bild 3).
Bild 2: Auswahl der Datenquelle
Bild 3: Auswahl des Typs Code First aus Datenbank
Danach kehren wir zum Dialog Assistent für Entity Data Model zurück, der Sie fragt, ob Sie Benutzername und Kennwort wirklich in der Verbindungszeichenfolge speichern wollen (siehe Bild 4). Für dieses Beispiel lassen wir diese Daten in der Verbindungszeichenfolge, aber im Artikel Zugangsdaten für SQL Server abfragen zeigen wir, wie Sie diese jeweils beim Start der Anwendung abfragen können.
Bild 4: Einstellen weiterer Eigenschaften
Schließlich wählen wir im Schritt Wählen Sie Ihre Datenbankobjekte und Einstellungen noch die Tabelle aus, deren Daten wir in der Anwendung anzeigen wollen. Wir haben die Tabelle tblFotos einfach in eine bestehende Datenbank eingefügt, daher müssen wir diese einzeln auswählen (siehe Bild 5).
Bild 5: Auswahl der gewünschten TabelleDieser Dialog bietet auch noch die Option Generierte Objektnamen in den Singular oder Plural setzen an. Da die Tabelle ohnehin das Präfix tbl im Namen tblFotos enthält, das wir in unserem Entity Data Model nicht als Bezeichnung der Entität sehen wollen, müssen wir hier ohnehin noch ein Mapping vornehmen.
Nach einem Klick auf die Schaltfläche Fertigstellen finden Sie zwei neue Elemente im Projektmappen-Explorer vor: die Klasse FotoverwaltungContext.vb, die von der Klasse DbContext erbt und zum Beispiel das DbSet namens tblFotos für den Zugriff auf die Liste der Entitäten auf Basis der in der Tabelle gespeicherten Daten bereitstellt sowie die Klasse tblFotos.vb als Entitätsklasse für einen Datensatz der Tabelle tblFotos.
Die Benennung der Klasse und des Namens des DbSet-Elements entspricht nicht unseren Vorstellungen, also ändern wir das ein wenig ab. Wie das genau gelingt, zeigen wir Ihnen im Artikel EDM für bestehende Datenbank mit Code First. Zum schnellen Nachbauen hier die notwendigen Änderungen. Als Erstes ändern wir den Namen der Klasse tblFotos.vb in Foto.vb.
In der Klasse Foto.vb müssen wir den Namen der Eigenschaft Foto in Fotodaten ändern, da keine Eigenschaft genauso heißen darf wie die Klasse:
Partial Public Class Foto
Public Property ID As Integer
<Column(TypeName:="image")>
<Required>
Public Property Fotodaten As Byte()
End Class
Damit die Klasse Foto auf die Tabelle tblFotos gemappt wird und die Eigenschaft Fotodaten der Klasse auf das Feld Foto der Tabelle, fügen wir folgende Zeilen zur Methode OnModelCreating der Klasse FotoverwaltungContext.vb hinzu:
Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
modelBuilder.Entity(Of Foto)().
ToTable("tblFotos").
Property(Function(t) t.Fotodaten).HasColumnName("Foto")
End Sub
Damit können wir nun testen, ob der Zugriff auf die Datensätze der Tabelle tblFotos gelingt. Dazu fügen wir der Klasse MainWindow.xaml.vb zunächst eine Konstruktor-Methode hinzu. In dieser erstellen wir einen Datenbankkontext für den Zugriff auf die Datenbank. Außerdem versuchen wir, auf das erste Element der Auflistung Fotos dieses Kontextes zuzugreifen und den Inhalt der Eigenschaften ID und Fotodaten in einem Meldungsfenster auszugeben:
Class MainWindow
Public Sub New()
Dim dbContext As FotoverwaltungContext
dbContext = New FotoverwaltungContext
Dim foto As Foto
foto = dbContext.Fotos.First()
MessageBox.Show(foto.ID.ToString() + vbCrLf + vbCrLf + foto.Fotodaten.ToString())
End Sub
End Class
Sofern Sie bereits einen Datensatz zu dieser Tabelle hinzugefügt haben, werden die entsprechenden Informationen beim Start der Anwendung ausgegeben.
Bild im WPF-Fenster anzeigen
Nun schauen wir uns an, wie wir den Inhalt der Eigenschaft Fotodaten, also des Feldes Foto der Tabelle tblFotos, in einem Steuerelement in einem WPF-Fenster anzeigen. Dazu benötigen wir zunächst ein geeignetes Steuerelement. Wir fügen dem Fenster MainWindow.xaml zunächst ein Image-Steuerelement hinzu, das wir mit dem Bild aus der Eigenschaft Fotodaten füllen wollen. Das Steuerelement ziehen wir einfach aus der Toolbox in das WPF-Fenster. Dadurch entsteht etwa der folgende XAML-Code:
<Image HorizontalAlignment="Left" Height="288" Margin="112,50,0,0" VerticalAlignment="Top" Width="364"/>
Das Bild ist im Feld Foto der Tabelle tblFotos unter dem Datentyp image gespeichert (siehe Bild 6). Wir müssen nun einen Weg finden, den Inhalt dieses Feldes im Image-Steuerelement anzuzeigen.
Bild 6: Das Feld Foto in der Tabelle tblFotosDazu verarbeiten wir dieses zunächst in der Code behind-Klasse MainWindow.xaml.vb. Hier verschieben wir die Deklaration des Datenbankkontexts zunächst aus der Konstruktor-Methode in den allgemeinen Teil der Klasse:
Class MainWindow
Dim dbContext As FotoverwaltungContext
Dann deklarieren wir eine private Variable namens m_Fotodaten mit dem Datentyp BitmapImage, die wir über die folgende Property namens Fotodaten mit dem gleichen Datentyp für den lesenden und schreibenden Zugriff verfügbar machen:
Dim m_Fotodaten As BitmapImage
Public Property Fotodaten As BitmapImage
Get
Return m_Fotodaten
End Get
Set(value As BitmapImage)
m_Fotodaten = value
End Set
End Property
Danach erweitern wir die Konstruktor-Methode New. Nach dem Zuweisen des Datenbankkontextes zur Variablen dbContext deklarieren wir ein Objekt des Typs Foto und weisen diesem das erste Element der mit der Auflistung Fotots eingelesenen Tabelle tblFotos zu:
Public Sub New()
dbContext = New FotoverwaltungContext
Dim foto As Foto
foto = dbContext.Fotos.First()
Dann lesen wir zunächst den Inhalt der Eigenschaft Fotodaten (also eigentlich des Feldes Foto der Tabelle tblFotos) in ein Objekt des Typs Byte mit dem Namen blob ein. Danach erstellen wir ein Objekt namens stream mit dem Typ MemoryStream, in das wir den Inhalt des Byte-Arrays blob mit der Write-Methode schreiben. Dann stellen wir die Position des Stream-Objekts auf die Position 0 ein:
Dim blob As Byte() = foto.Fotodaten
Dim stream As MemoryStream = New MemoryStream()
stream.Write(blob, 0, blob.Length)
stream.Position = 0
Nun kommt erstmals das Objekt des Typs BitmapImage ins Spiel, das wir anschließend auch dem Image-Steuerelement als Quelle zuweisen können. Dieses erstellen wir unter dem Namen img und initialisieren es mit der Methode BeginInit. Dann weisen wir als StreamSource den Inhalt der MemoryStream-Variablen stream zu und beenden die Initialisierung von img mit der EndInit-Methode:
Dim img As BitmapImage
img = New BitmapImage
img.BeginInit()
img.StreamSource = stream
img.EndInit()
Schließlich weisen wir img der öffentlichen Eigenschaft Fotodaten zu und stellen die aktuelle Klasse als DataContext der WPF-Seite ein:
Fotodaten = img
DataContext = Me
End Sub
End Class
Danach müssen wir nur noch der Eigenschaft Source des Image-Elements im XAML-Code den Pfad zur Eigenschaft Fotodaten mitteilen:
<Image HorizontalAlignment="Left" Height="288" Margin="112,50,0,0" VerticalAlignment="Top" Width="364" Source="{Binding Path=Fotodaten}"/>
*****Bild mit Bild*****
Durch Bilder blättern
Wenn wir nun nicht nur ein Bild anzeigen, sondern auch durch alle vorhandenen Bilder blättern wollen, benötigen wir prinzipiell die gleichen Techniken, die wir auch sonst zum Blättern in Datensätzen verwenden. Dazu erstellen wir ein neues Fenster namens FotosBlaettern.xaml. Dieses öffnen wir vom Startfenster MainWindow.xaml aus mit einer neuen Schaltfläche, der wir die folgenden Codezeilen hinzufügen:
Private Sub btnBeispielZumBlaettern_Click(sender As Object, e As RoutedEventArgs)
Dim wnd As FotosBlaettern
wnd = New FotosBlaettern
wnd.ShowDialog()
End Sub
Die Techniken, die wir weiter oben kennengelernt haben, können wir für das Blättern durch Datensätze mit Bildern ebenfalls verwenden. Außerdem nutzen wir einige Techniken, die wir im Artikel EDM: Blättern in Datensätzen vorgestellt haben. Im XAML-Code verwenden wir in den Ressourcen einige DataTrigger, mit denen wir die Schaltflächen aktivieren und deaktivieren wollen (siehe ebenfalls im oben genannten Artikel):
<Window x:Class="FotosBlaettern" ... Title="FotosBlaettern" Height="450" Width="800">
<Window.Resources>
<Style x:Key="ButtonVorheriger" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Erster}" Value="False">
<Setter Property="IsEnabled" Value="False"></Setter>
</DataTrigger>
Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.