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.
In dieser Schritt-für-Schritt-Anleitung wird gezeigt, wie POCO-Typen in einem "Master-Detail"-Formular an WPF-Steuerelemente 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 teilnehmen: Kategorie (Haupt) und Produkt (abhängig/Detail). 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 2019 16.6.5.
Tipp
Sie können das Beispiel dieses Artikels auf GitHubanzeigen.
Voraussetzungen
Sie müssen Visual Studio 2019 16.3 oder höher mit ausgewählter .NET-Desktoparbeitsauslastung installiert haben, um diese exemplarische Vorgehensweise abzuschließen. Weitere Informationen zum Installieren der neuesten Version von Visual Studio finden Sie unter Installieren von Visual Studio.
Erstellen der Anwendung
- Öffnen Sie Visual Studio.
- Wählen Sie im Startfenster " Neues Projekt erstellen" aus.
- Suchen Sie nach "WPF", wählen Sie WPF-App (.NET Core) und dann "Weiter" aus.
- Geben Sie auf dem nächsten Bildschirm dem Projekt einen Namen, z. B. "GetStartedWPF", und wählen Sie "Erstellen" aus.
Installieren der Entity Framework-NuGet-Pakete
Klicken Sie mit der rechten Maustaste auf die Lösung, und wählen Sie "NuGet-Pakete für Lösung verwalten" aus...
Geben Sie im Suchfeld als Suchbegriff
entityframeworkcore.sqlite
ein.Wählen Sie das Microsoft.EntityFrameworkCore.Sqlite-Paket aus.
Überprüfen Sie das Projekt im rechten Bereich, und klicken Sie auf "Installieren".
Wiederholen Sie die Schritte, um
entityframeworkcore.proxies
zu suchen und Microsoft.EntityFrameworkCore.Proxies zu installieren.
Hinweis
Wenn Sie das Sqlite-Paket installiert haben, hat es automatisch das zugehörige Microsoft.EntityFrameworkCore-Basispaket abgerufen. Das Microsoft.EntityFrameworkCore.Proxies-Paket bietet Unterstützung für "lazy-loading"-Daten. Dies bedeutet, dass, wenn Sie Entitäten mit untergeordneten Entitäten haben, nur die übergeordneten Entitäten während des ersten Ladevorgangs abgerufen werden. Die Proxys erkennen, wann versucht wird, auf die untergeordneten Entitäten zuzugreifen, und laden sie bei Bedarf automatisch.
Definieren eines Modells
In dieser exemplarischen Vorgehensweise implementieren Sie ein Modell mit "Code zuerst". Dies bedeutet, dass EF Core die Datenbanktabellen und das Schema basierend auf den von Ihnen definierten C#-Klassen erstellt.
Fügen Sie eine neue Klasse hinzu. Geben Sie ihm den Namen: Product.cs
und füllen Sie ihn wie folgt auf:
Product.cs
namespace GetStartedWPF
{
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 als Nächstes eine Benannte Category.cs
Klasse hinzu, und füllen Sie sie mit dem folgenden Code auf:
Category.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace GetStartedWPF
{
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product>
Products
{ get; private set; } =
new ObservableCollection<Product>();
}
}
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.cs
Klasse mit der folgenden Definition hinzu:
ProductContext.cs
using Microsoft.EntityFrameworkCore;
namespace GetStartedWPF
{
public class ProductContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(
"Data Source=products.db");
optionsBuilder.UseLazyLoadingProxies();
}
}
}
-
DbSet
informiert EF Core darüber, welche C#-Entitäten der Datenbank zugeordnet werden sollen. - Es gibt eine Vielzahl von Möglichkeiten zum Konfigurieren des EF Core
DbContext
. Informationen dazu finden Sie unter: Konfigurieren eines DbContexts. - In diesem Beispiel wird die
OnConfiguring
Überschreibung verwendet, um eine Sqlite-Datendatei anzugeben. - Der
UseLazyLoadingProxies
Aufruf weist EF Core an, Lazy-Loading zu implementieren, sodass untergeordnete Objekte beim Zugriff von übergeordneten Elementen automatisch geladen werden.
Drücken Sie STRG+UMSCHALT+B oder navigieren Sie zu Erstellen > Lösung erstellen, um das Projekt zu kompilieren.
Tipp
Erfahren Sie mehr über die unterschiedlichen Möglichkeiten, um Ihre Datenbank und EF Core-Modelle synchron zu halten: Verwalten von Datenbankschemas.
Faules Laden
Die Products-Eigenschaft der Category-Klasse und die Category-Eigenschaft der Product-Klasse sind Navigationseigenschaften. In Entity Framework Core bieten Navigationseigenschaften eine Möglichkeit zum Navigieren in einer Beziehung zwischen zwei Entitätstypen.
EF Core 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 Verwendung von "Plain Old C# Object" (POCO)-Entitätstypen erzielt EF Core Lazy Loading, indem während der Laufzeit Instanzen abgeleiteter Proxytypen erstellt und dann virtuelle Eigenschaften in Ihren Klassen überschrieben werden, um das Laden zu ermöglichen. Um Lazy-Loading von verwandten Objekten zu erreichen, müssen Sie Navigationseigenschafts-Getter als öffentlich und virtuell (in Visual Basic überschreibbar) deklarieren, und Ihre Klasse darf nicht versiegelt sein (NotOverridable in Visual Basic). Bei Verwendung von Database First werden Navigationseigenschaften automatisch virtual gemacht, um Lazy Loading zu ermöglichen.
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 die XAML-Registerkarte aus, um den XAML-Code zu bearbeiten.
Fügen Sie unmittelbar nach dem öffnenden
Window
Tag die folgenden Quellen hinzu, um eine Verbindung mit den EF Core-Entitäten herzustellen.<Window x:Class="GetStartedWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:GetStartedWPF" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded"> <Window.Resources> <CollectionViewSource x:Key="categoryViewSource"/> <CollectionViewSource x:Key="categoryProductsViewSource" Source="{Binding Products, Source={StaticResource categoryViewSource}}"/> </Window.Resources>
Dadurch wird die Quelle für die "übergeordneten" Kategorien eingerichtet, sowie eine zweite Quelle für die "Detail"-Produkte.
Fügen Sie als Nächstes nach dem öffnenden
Grid
Tag das folgende Markup zu Ihrem XAML-Code hinzu.<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding Source={StaticResource categoryViewSource}}" Margin="13,13,43,229" RowDetailsVisibilityMode="VisibleWhenSelected"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding CategoryId}" Header="Category Id" Width="SizeToHeader" IsReadOnly="True"/> <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/> </DataGrid.Columns> </DataGrid>
Beachten Sie, dass
CategoryId
aufReadOnly
gesetzt ist, weil es von der Datenbank zugewiesen wird und nicht geändert werden kann.
Hinzufügen eines Detailrasters
Nachdem das Raster zum Anzeigen von Kategorien vorhanden ist, kann das Detailraster hinzugefügt werden, um Produkte anzuzeigen. Fügen Sie dies innerhalb des Grid
Elements nach dem Categories-Element DataGrid
hinzu.
MainWindow.xaml
<DataGrid x:Name="productsDataGrid" AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource categoryProductsViewSource}}"
Margin="13,205,43,108" RowDetailsVisibilityMode="VisibleWhenSelected"
RenderTransformOrigin="0.488,0.251">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id"
Width="SizeToHeader" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Fügen Sie schließlich eine Save
Schaltfläche hinzu, und verknüpfen Sie das Click-Ereignis mit Button_Click
.
<Button Content="Save" HorizontalAlignment="Center" Margin="0,240,0,0"
Click="Button_Click" Height="20" Width="123"/>
Ihre Entwurfsansicht sollte 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> , um das Hauptfenster auszuwählen.
Wählen Sie im Fenster "Eigenschaften"die Option "Ereignisse " oben rechts aus, und doppelklicken Sie dann auf das Textfeld rechts neben der Beschriftung "Geladen ".
Dadurch gelangen Sie zum Code-Behind des Formulars. Wir werden nun den Code bearbeiten, um den ProductContext
zu verwenden und damit Datenzugriff durchzuführen. Aktualisieren Sie den Code wie unten dargestellt.
Der Code deklariert eine lang andauernde Instanz von ProductContext
. Das ProductContext
Objekt wird verwendet, um Daten in der Datenbank abzufragen und zu speichern. Die Dispose()
Methode für die ProductContext
Instanz wird dann von der überschriebenen OnClosing
Methode aufgerufen. In den Codekommentaren wird erläutert, was jeder Schritt tut.
MainWindow.xaml.cs
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace GetStartedWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly ProductContext _context =
new ProductContext();
private CollectionViewSource categoryViewSource;
public MainWindow()
{
InitializeComponent();
categoryViewSource =
(CollectionViewSource)FindResource(nameof(categoryViewSource));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// this is for demo purposes only, to make it easier
// to get up and running
_context.Database.EnsureCreated();
// load the entities into EF Core
_context.Categories.Load();
// bind to the source
categoryViewSource.Source =
_context.Categories.Local.ToObservableCollection();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// all changes are automatically tracked, including
// deletes!
_context.SaveChanges();
// this forces the grid to refresh to latest values
categoryDataGrid.Items.Refresh();
productsDataGrid.Items.Refresh();
}
protected override void OnClosing(CancelEventArgs e)
{
// clean up database connections
_context.Dispose();
base.OnClosing(e);
}
}
}
Hinweis
Der Code verwendet einen Aufruf zum EnsureCreated()
Erstellen der Datenbank bei der ersten Ausführung. Dies ist für Demos akzeptabel, aber in Produktions-Apps sollten Sie sich Migrationen ansehen, um Ihr Schema zu verwalten. Der Code wird auch synchron ausgeführt, da er eine lokale SQLite-Datenbank verwendet. Für Produktionsszenarien, die in der Regel einen Remoteserver umfassen, sollten Sie die asynchronen Versionen der Load
Und SaveChanges
Methoden in Betracht ziehen.
Testen der WPF-Anwendung
Kompilieren Sie die Anwendung, und führen Sie sie aus, indem Sie F5 drücken oder "Debuggen > starten" auswählen. Die Datenbank sollte automatisch mit einer Datei mit dem Namen products.db
erstellt werden. Geben Sie einen Kategorienamen ein, und drücken Sie die EINGABETASTE, und fügen Sie dann Produkte zum unteren Raster hinzu. Klicken Sie auf "Speichern", und beobachten Sie die Rasteraktualisierung mit den bereitgestellten IDs der Datenbank. Markieren Sie eine Zeile und drücken Sie Entf, um die Zeile zu entfernen. Die Entität wird gelöscht, wenn Sie auf "Speichern" klicken.
Eigenschaftsänderungsbenachrichtigung
In diesem Beispiel werden vier Schritte verwendet, um die Entitäten mit der Benutzeroberfläche zu synchronisieren.
- Der anfängliche Aufruf
_context.Categories.Load()
lädt die Kategoriendaten. - Die Lazy-Loading-Proxies laden die Daten zu den abhängigen Produkten.
- Die integrierte Änderungsnachverfolgung von EF Core führt bei Aufruf von
_context.SaveChanges()
die notwendigen Anpassungen an Entitäten durch, einschließlich Einfügungen und Löschungen. - Die Aufrufe zu
DataGridView.Items.Refresh()
führen zu einem Neuladen mit den neu generierten IDs.
Dies funktioniert für unser Beispiel für die ersten Schritte, aber Sie benötigen möglicherweise zusätzlichen Code für andere Szenarien. WPF-Steuerelemente rendern die Benutzeroberfläche, indem die Felder und Eigenschaften von Ihren Entitäten gelesen werden. Wenn Sie einen Wert auf der Benutzeroberfläche bearbeiten, wird dieser Wert an Ihre Entität übergeben. Wenn Sie den Wert einer Eigenschaft direkt in Ihrer Entität ändern, z. B. das Laden aus der Datenbank, spiegelt WPF nicht sofort die Änderungen in der Benutzeroberfläche wider. Das Renderingmodul muss über die Änderungen benachrichtigt werden. Das Projekt hat dies durch das manuelle Aufrufen von Refresh()
getan. Eine einfache Möglichkeit zum Automatisieren dieser Benachrichtigung ist die Implementierung der INotifyPropertyChanged-Schnittstelle . WPF-Komponenten erkennen die Schnittstelle automatisch und registrieren sich für Änderungsereignisse. Die Entität ist für das Auslösen dieser Ereignisse verantwortlich.
Tipp
Weitere Informationen zum Behandeln von Änderungen finden Sie unter: Implementieren von Benachrichtigungen zur Änderung von Eigenschaften.
Nächste Schritte
Erfahren Sie mehr über das Konfigurieren eines DbContext-