Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Important
Dieses Dokument ist nur für WPF im .NET Framework gültig.
Dieses Dokument beschreibt die Datenbindung für WPF im .NET Framework. Für neue .NET-Projekte empfehlen wir, EF Core anstelle von Entity Framework 6 zu verwenden. Die Dokumentation für die Datenbindung in EF Core ist hier: Erste Schritte mit WPF.
In dieser schrittweisen exemplarischen Vorgehensweise wird gezeigt, wie POCO-Typen an WPF-Steuerelemente in einem „master-detail“-Formular gebunden werden. Die Anwendung verwendet die Entity Framework-APIs, um Objekte mit Daten aus der Datenbank aufzufüllen, Änderungen nachzuverfolgen und Daten in der Datenbank beizubehalten.
Das Modell definiert zwei Typen, die an einer 1:n-Beziehung beteiligt sind: Category (principal\master) und Product (dependent\detail). Anschließend werden die Visual Studio-Tools verwendet, um die im Modell definierten Typen an die WPF-Steuerelemente zu binden. Das WPF-Datenbindungsframework ermöglicht die Navigation zwischen verwandten Objekten: Das Auswählen von Zeilen in der Masteransicht bewirkt, dass die Detailansicht mit den entsprechenden untergeordneten Daten aktualisiert wird.
Die Screenshots und Codeauflistungen in dieser exemplarischen Vorgehensweise stammen aus Visual Studio 2013, aber Sie können diese exemplarische Vorgehensweise mit Visual Studio 2012 oder Visual Studio 2010 ausführen.
Verwenden der Option "Objekt" zum Erstellen von WPF-Datenquellen
Mit früherer Version von Entity Framework wurde die Verwendung der Datenbankoption beim Erstellen einer neuen Datenquelle basierend auf einem Modell empfohlen, das mit dem EF-Designer erstellt wurde. Dies liegt daran, dass der Designer einen Kontext generierte, der von ObjectContext und Entitätsklassen abgeleitet wurde, die von EntityObject abgeleitet wurden. Wenn Sie die Datenbankoption verwenden, können Sie den besten Code für die Interaktion mit dieser API-Oberfläche schreiben.
Die EF Designer für Visual Studio 2012 und Visual Studio 2013 generieren einen Kontext, der von DbContext zusammen mit einfachen POCO-Entitätsklassen abgeleitet wird. In Visual Studio 2010 wird empfohlen, eine Codegenerierungsvorlage zu verwenden, die DbContext verwendet, wie weiter unten in dieser exemplarischen Vorgehensweise beschrieben.
Wenn Sie die DbContext-API-Oberfläche verwenden, sollten Sie beim Erstellen einer neuen Datenquelle die Option "Objekt " verwenden, wie in dieser exemplarischen Vorgehensweise gezeigt.
Bei Bedarf können Sie die ObjektContext-basierte Codegenerierung für Modelle wiederherstellen, die mit dem EF Designer erstellt wurden.
Pre-Requisites
Sie müssen Visual Studio 2013, Visual Studio 2012 oder Visual Studio 2010 installiert haben, um diese exemplarische Vorgehensweise abzuschließen.
Wenn Sie Visual Studio 2010 verwenden, müssen Sie auch NuGet installieren. Weitere Informationen finden Sie unter Installieren von NuGet.
Erstellen der Anwendung
- Öffnen Sie Visual Studio.
- Datei -> Neu -> Projekt....
- Auswählen von Windows im linken Bereich und WPFApplication im rechten Bereich
- Geben Sie WPFwithEFSample als Namen ein.
- Wählen Sie OK aus.
Installieren des Entity Framework NuGet-Pakets
- Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das WinFormswithEFSample-Projekt
- Wählen Sie NuGet-Pakete verwalten... aus.
- Wählen Sie im Dialogfeld "NuGet-Pakete verwalten" die Registerkarte "Online " und dann das EntityFramework-Paket aus.
- Klicken Sie auf Install (Installieren).
Note
Zusätzlich zur EntityFramework-Assembly wird auch ein Verweis auf System.ComponentModel.DataAnnotations hinzugefügt. Wenn das Projekt über einen Verweis auf System.Data.Entity verfügt, wird es beim Installieren des EntityFramework-Pakets entfernt. Die System.Data.Entity-Assembly wird nicht mehr für Entity Framework 6-Anwendungen verwendet.
Definieren eines Modells
In dieser exemplarischen Vorgehensweise können Sie ein Modell mithilfe von Code First oder EF Designer implementieren. Führen Sie einen der beiden folgenden Abschnitte aus.
Option 1: Definieren eines Modells mithilfe von Code Zuerst
In diesem Abschnitt wird gezeigt, wie Sie ein Modell und die zugehörige Datenbank mithilfe von Code First erstellen. Fahren Sie mit dem nächsten Abschnitt (Option 2: Definieren eines Modells mithilfe von Database First) fort, wenn Sie lieber Database First verwenden möchten, um das Modell mithilfe des EF-Designers aus einer Datenbank zu reverse-engineeren.
Beim Verwenden der Code First-Entwicklung beginnen Sie in der Regel mit dem Schreiben von .NET Framework-Klassen, die Ihr konzeptionelles (Domänen)-Modell definieren.
- Fügen Sie der WPFwithEFSample eine neue Klasse hinzu:
- Klicken Sie mit der rechten Maustaste auf den Projektnamen.
- Wählen Sie "Hinzufügen" und dann "Neues Element" aus.
- Wählen Sie "Klasse" aus, und geben Sie " Produkt " für den Klassennamen ein.
- Ersetzen Sie die Produktklassendefinition durch den folgenden Code:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
- Fügen Sie eine Category-Klasse mit der folgenden Definition hinzu:
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Product> Products { get; private set; }
}
}
Die Products-Eigenschaft der Category-Klasse und die Category-Eigenschaft der Product-Klasse sind Navigationseigenschaften. In Entity Framework bieten Navigationseigenschaften eine Möglichkeit zum Navigieren in einer Beziehung zwischen zwei Entitätstypen.
Zusätzlich zum Definieren von Entitäten müssen Sie eine Klasse definieren, die von DbContext abgeleitet wird und DbSet<TEntity-Eigenschaften> verfügbar macht. Die DbSet<TEntity-Eigenschaften> lassen den Kontext wissen, welche Typen in das Modell eingeschlossen werden sollen.
Eine Instanz des vom DbContext abgeleiteten Typs verwaltet die Entitätsobjekte während der Laufzeit, einschließlich auffüllen von Objekten mit Daten aus einer Datenbank, Änderungsnachverfolgung und Beibehalten von Daten in der Datenbank.
- Fügen Sie dem Projekt eine neue ProductContext-Klasse mit der folgenden Definition hinzu:
using System.Data.Entity;
namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}
Kompilieren Sie das Projekt.
Option 2: Definieren eines Modells mithilfe der Datenbank zuerst
In diesem Abschnitt wird gezeigt, wie Sie mit Database First Ihr Modell aus einer Datenbank mithilfe des EF-Designers rückentwickeln. Wenn Sie den vorherigen Abschnitt abgeschlossen haben (Option 1: Definieren eines Modells mithilfe von Code First), überspringen Sie diesen Abschnitt, und wechseln Sie direkt zum Abschnitt "Lazy Loading ".
Erstellen einer vorhandenen Datenbank
In der Regel, wenn Sie auf eine vorhandene Datenbank abzielen, wird sie bereits erstellt, aber für diese exemplarische Vorgehensweise müssen wir eine Datenbank erstellen, auf die zugegriffen werden kann.
Der Datenbankserver, der mit Visual Studio installiert ist, unterscheidet sich je nach installierter Version von Visual Studio:
- Wenn Sie Visual Studio 2010 verwenden, erstellen Sie eine SQL Express-Datenbank.
- Wenn Sie Visual Studio 2012 verwenden, erstellen Sie eine LocalDB-Datenbank .
Lassen Sie uns fortfahren und die Datenbank generieren.
Ansicht –> Server-Explorer
Klicken Sie mit der rechten Maustaste auf Datenverbindungen –> Verbindung hinzufügen...
Wenn Sie im Server-Explorer noch keine Verbindung mit einer Datenbank hergestellt haben, müssen Sie Microsoft SQL Server als Datenquelle auswählen.
Stellen Sie eine Verbindung mit LocalDB oder SQL Express her, je nachdem, welches Sie installiert haben, und geben Sie "Produkte " als Datenbanknamen ein.
Wählen Sie "OK" aus, und Sie werden gefragt, ob Sie eine neue Datenbank erstellen möchten, wählen Sie "Ja" aus.
Die neue Datenbank wird nun im Server-Explorer angezeigt, klicken Sie mit der rechten Maustaste darauf, und wählen Sie "Neue Abfrage" aus.
Kopieren Sie die folgende SQL-Datei in die neue Abfrage, klicken Sie dann mit der rechten Maustaste auf die Abfrage, und wählen Sie "Ausführen" aus.
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
Zurückentwickeln (Reverse Engineering) des Modells
Wir verwenden den Entity Framework-Designer, der als Teil von Visual Studio enthalten ist, um unser Modell zu erstellen.
Projekt –> Neues Element hinzufügen...
Wählen Sie Daten im linken Menü aus und dann ADO.NET Entity Data Model
Geben Sie "ProductModel " als Namen ein, und klicken Sie auf "OK".
Dadurch wird der Entitätsdatenmodell-Assistent gestartet.
Wählen Sie "Aus Datenbank generieren " aus, und klicken Sie auf "Weiter".
Wählen Sie die Verbindung mit der Datenbank aus, die Sie im ersten Abschnitt erstellt haben, geben Sie "ProductContext" als Namen der Verbindungszeichenfolge ein, und klicken Sie auf "Weiter".
Klicken Sie auf das Kontrollkästchen neben 'Tabellen', um alle Tabellen zu importieren, und klicken Sie auf 'Fertig stellen'.
Sobald der Reverse Engineering-Prozess abgeschlossen ist, wird das neue Modell zu Ihrem Projekt hinzugefügt und geöffnet, damit Sie im Entity Framework Designer anzeigen können. Außerdem wurde Ihrem Projekt eine App.config Datei mit den Verbindungsdetails für die Datenbank hinzugefügt.
Zusätzliche Schritte in Visual Studio 2010
Wenn Sie in Visual Studio 2010 arbeiten, müssen Sie den EF-Designer aktualisieren, um ef6-Codegenerierung zu verwenden.
- Klicken Sie im EF-Designer mit der rechten Maustaste auf eine leere Stelle Ihres Modells, und wählen Sie " Codegenerierungselement hinzufügen" aus...
- Wählen Sie im linken Menü "Onlinevorlagen" aus, und suchen Sie nach "DbContext".
- Wählen Sie den EF 6.x DbContext-Generator für C# aus, geben Sie "ProductsModel " als Namen ein, und klicken Sie auf "Hinzufügen".
Aktualisieren der Codegenerierung für die Datenbindung
EF generiert Code aus Ihrem Modell mithilfe von T4-Vorlagen. Die vorlagen, die mit Visual Studio ausgeliefert oder aus dem Visual Studio-Katalog heruntergeladen wurden, sind für die allgemeine Verwendung vorgesehen. Dies bedeutet, dass die aus diesen Vorlagen generierten Entitäten einfache ICollection<T-Eigenschaften> aufweisen. Beim Verwenden der Datenbindung mit WPF ist es jedoch wünschenswert, ObservableCollection für Sammlungseigenschaften zu verwenden, damit WPF änderungen nachverfolgen kann, die an den Auflistungen vorgenommen wurden. Zu diesem Zweck werden wir die Vorlagen so ändern, dass ObservableCollection verwendet wird.
Öffnen Sie den Projektmappen-Explorer , und suchen Sie die Datei "ProductModel.edmx "
Suchen Sie die ProductModel.tt Datei, die unter der Datei "ProductModel.edmx" geschachtelt wird.
Doppelklicken Sie auf die ProductModel.tt Datei, um sie im Visual Studio-Editor zu öffnen.
Suchen und ersetzen Sie die beiden Vorkommen von "ICollection" durch "ObservableCollection". Diese befinden sich etwa in den Linien 296 und 484.
Suchen und ersetzen Sie das erste Vorkommen von "HashSet" durch "ObservableCollection". Dieses Vorkommen befindet sich ungefähr in Zeile 50. Ersetzen Sie nicht das zweite Vorkommen von HashSet, das später im Code gefunden wurde.
Suchen und ersetzen Sie das einzige Vorkommen von "System.Collections.Generic" durch "System.Collections.ObjectModel". Dies befindet sich ungefähr an der Linie 424.
Speichern Sie die ProductModel.tt Datei. Dies sollte dazu führen, dass der Code für Entitäten neu generiert wird. Wenn der Code nicht automatisch neu generiert wird, klicken Sie mit der rechten Maustaste auf ProductModel.tt, und wählen Sie "Benutzerdefiniertes Tool ausführen" aus.
Wenn Sie nun die Category.cs-Datei (die unter ProductModel.tt geschachtelt ist) öffnen, sollten Sie sehen, dass die Products-Auflistung den Typ ObservableCollection-Produkt<> aufweist.
Kompilieren Sie das Projekt.
Faules Laden
Die Products-Eigenschaft der Category-Klasse und die Category-Eigenschaft der Product-Klasse sind Navigationseigenschaften. In Entity Framework bieten Navigationseigenschaften eine Möglichkeit zum Navigieren in einer Beziehung zwischen zwei Entitätstypen.
EF bietet Ihnen die Möglichkeit, verwandte Entitäten automatisch aus der Datenbank zu laden, wenn Sie zum ersten Mal auf die Navigationseigenschaft zugreifen. Beachten Sie bei diesem Ladetyp (als faules Laden bezeichnet), dass beim ersten Zugriff auf jede Navigationseigenschaft eine separate Abfrage für die Datenbank ausgeführt wird, wenn sich der Inhalt nicht bereits im Kontext befindet.
Bei der Verwendung von POCO-Entitätstypen erreicht EF Lazy Loading, indem während der Laufzeit Instanzen abgeleiteter Proxytypen erstellt und dann virtuelle Eigenschaften in Ihren Klassen überschrieben werden, um den Ladehook hinzuzufügen. Um Lazy Loading von verwandten Objekten zu erzielen, müssen Sie Navigationseigenschaftsgetter als public und virtual deklarieren (Overridable in Visual Basic), und Ihre Klasse darf nicht sealed (NotOverridable in Visual Basic) sein. Bei der Verwendung von Database First werden Navigationseigenschaften automatisch virtuell gemacht, um Lazy Loading zu ermöglichen. Im Abschnitt "Code First" haben wir die Navigationseigenschaften aus demselben Grund virtualisiert.
Objekt an Steuerelemente binden
Fügen Sie die Klassen hinzu, die im Modell als Datenquellen für diese WPF-Anwendung definiert sind.
Doppelklicken Sie im Projektmappen-Explorer auf "MainWindow.xaml" , um das Hauptformular zu öffnen.
Wählen Sie im Hauptmenü "Projekt> - Neue Datenquelle hinzufügen" aus ... (in Visual Studio 2010 müssen Sie "Daten>" auswählen – Neue Datenquelle hinzufügen...)
Wählen Sie im Bereich "Datenquellentyp auswählen" " Objekt " aus, und klicken Sie auf "Weiter".
Erweitern Sie im Dialogfeld "Datenobjekte auswählen" das WPFwithEFSample zweimal, und wählen Sie "Kategorie" aus.
Es ist nicht erforderlich, die Datenquelle "Produkt " auszuwählen, da wir sie über die Eigenschaft des Produkts in der Kategorie-Datenquelle aufrufen.
Klicken Sie auf "Fertig stellen".
Das Fenster "Datenquellen" wird neben dem Fenster "MainWindow.xaml" geöffnet, wenn das Fenster "Datenquellen" nicht angezeigt wird, wählen Sie "Ansicht -> Andere Windows-Datenquellen>" aus.
Drücken Sie das Pinsymbol, sodass das Fenster "Datenquellen" nicht automatisch ausgeblendet wird. Möglicherweise müssen Sie auf die Aktualisierungsschaltfläche klicken, wenn das Fenster bereits sichtbar war.
Wählen Sie die Kategoriedatenquelle aus, und ziehen Sie sie auf das Formular.
Die folgenden Ereignisse sind passiert, als wir diese Quelle verschoben haben:
- Die categoryViewSource-Ressource und das CategoryDataGrid-Steuerelement wurden XAML hinzugefügt.
- Die DataContext-Eigenschaft für das übergeordnete Grid-Element wurde auf "{StaticResource categoryViewSource }" festgelegt. Die categoryViewSource-Ressource dient als Bindungsquelle für das äußere\übergeordnete Grid-Element. Die inneren Grid-Elemente erben dann den DataContext-Wert vom übergeordneten Grid (die ItemsSource-Eigenschaft von categoryDataGrid ist auf "{Binding}") festgelegt.
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Hinzufügen eines Detailrasters
Nachdem wir nun ein Raster zum Anzeigen von Kategorien haben, fügen wir ein Detailraster hinzu, um die zugehörigen Produkte anzuzeigen.
- Wählen Sie die Eigenschaft "Products " aus der Kategorie-Datenquelle aus, und ziehen Sie sie auf das Formular.
- Die categoryProductsViewSource-Ressource und das productDataGrid-Raster werden XAML hinzugefügt.
- Der Bindungspfad für diese Ressource ist auf "Products" festgelegt.
- Das WPF-Datenbindungsframework stellt sicher, dass nur Produkte im Zusammenhang mit der ausgewählten Kategorie in productDataGrid angezeigt werden.
- Ziehen Sie in der Toolbox die Schaltfläche auf das Formular. Legen Sie die Eigenschaft "Name" auf "buttonSave " und die Inhaltseigenschaft auf " Speichern" fest.
Das Formular sollte etwa wie folgt aussehen:
Hinzufügen von Code zur Behandlung von Dateninteraktionen
Es ist an der Zeit, dem Hauptfenster einige Ereignishandler hinzuzufügen.
Klicken Sie im XAML-Fenster auf das <Window-Element . Dadurch wird das Hauptfenster ausgewählt.
Wählen Sie im Fenster "Eigenschaften " die Option "Ereignisse " oben rechts aus, und doppelklicken Sie dann rechts neben der Beschriftung "Geladen" auf das Textfeld.
Fügen Sie außerdem das Click-Ereignis für die Schaltfläche "Speichern" hinzu, indem Sie im Designer auf die Schaltfläche " Speichern " doppelklicken.
Dadurch gelangen Sie zum Code-Behind des Formulars. Jetzt werden wir den Code bearbeiten, um den ProductContext für den Datenzugriff zu verwenden. Aktualisieren Sie den Code für "MainWindow" wie unten dargestellt.
Der Code deklariert eine langlaufende Instanz von ProductContext. Das ProductContext-Objekt wird verwendet, um Daten in der Datenbank abzufragen und zu speichern. Die Dispose() für die ProductContext-Instanz wird dann aus der überschriebenen OnClosing-Methode aufgerufen. Die Codekommentare enthalten Details zur Funktionsweise des Codes.
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoryViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));
// Load is an extension method on IQueryable,
// defined in the System.Data.Entity namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.Categories.Load();
// After the data is loaded call the DbSet<T>.Local property
// to use the DbSet<T> as a binding source.
categoryViewSource.Source = _context.Categories.Local;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.Products.Local.ToList())
{
if (product.Category == null)
{
_context.Products.Remove(product);
}
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
}
Testen der WPF-Anwendung
Kompilieren Sie die Anwendung, und führen Sie sie aus. Wenn Sie Code First verwendet haben, sehen Sie, dass für Sie eine WPFwithEFSample.ProductContext-Datenbank erstellt wird.
Geben Sie einen Kategorienamen in das obere Raster und Produktnamen im unteren Raster ein, geben Sie nichts in ID-Spalten ein, da der Primärschlüssel von der Datenbank generiert wird.
Drücken Sie die Schaltfläche " Speichern ", um die Daten in der Datenbank zu speichern.
Nach dem Aufruf der SaveChanges()-Methode von DbContext werden die IDs mit den generierten Werten der Datenbank aufgefüllt. Da wir Refresh() nach SaveChanges() aufgerufen haben, werden die DataGrid-Steuerelemente auch mit den neuen Werten aktualisiert.
Zusätzliche Ressourcen
Weitere Informationen zur Datenbindung an Sammlungen mit WPF finden Sie in diesem Thema in der WPF-Dokumentation.