Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym przewodniku krok po kroku pokazano, jak powiązać typy POCO z kontrolkami WPF w formularzu "main-detail". Aplikacja używa interfejsów API programu Entity Framework do wypełniania obiektów danymi z bazy danych, śledzenia zmian i utrwalania danych w bazie danych.
Model definiuje dwa typy, które uczestniczą w relacji jeden do wielu: Kategoria (główna\główna) i Produkt (podrzędny\szczegółowy). Struktura powiązania danych WPF umożliwia nawigację między powiązanymi obiektami: wybieranie wierszy w widoku głównym powoduje, że widok szczegółów zostanie zaktualizowany przy użyciu odpowiednich danych podrzędnych.
Zrzuty ekranu i listy kodu w tym przewodniku pochodzą z programu Visual Studio 2019 16.6.5.
Wskazówka
Wymagania wstępne
Aby ukończyć ten przewodnik, potrzebujesz zainstalowanego programu Visual Studio 2019 w wersji 16.3 lub nowszej z wybranym pakietem funkcji pulpitu .NET. Aby uzyskać więcej informacji na temat instalowania najnowszej wersji programu Visual Studio, zobacz Install Visual Studio.
Tworzenie aplikacji
- Otwórz program Visual Studio.
- W oknie uruchamiania wybierz pozycję Utwórz nowy projekt.
- Wyszukaj ciąg "WPF", wybierz pozycję Aplikacja WPF (.NET Core), a następnie wybierz pozycję Dalej.
- Na następnym ekranie nadaj projektowi nazwę, na przykład GetStartedWPF, a następnie wybierz pozycję Utwórz.
Zainstaluj pakiety NuGet programu Entity Framework
Kliknij prawym przyciskiem myszy rozwiązanie i wybierz polecenie Zarządzaj pakietami NuGet dla rozwiązania...
Wpisz
entityframeworkcore.sqlite
w polu wyszukiwania.Wybierz pakiet Microsoft.EntityFrameworkCore.Sqlite .
Sprawdź projekt w okienku po prawej stronie i kliknij pozycję Zainstaluj
Powtórz kroki, aby wyszukać
entityframeworkcore.proxies
i zainstalować pliki Microsoft.EntityFrameworkCore.Proxies.
Uwaga / Notatka
Po zainstalowaniu pakietu Sqlite automatycznie ściągnął powiązany pakiet podstawowy Microsoft.EntityFrameworkCore . Pakiet Microsoft.EntityFrameworkCore.Proxies zapewnia obsługę danych "ładowanych z opóźnieniem". Oznacza to, że jeśli masz jednostki z jednostkami podrzędnymi, tylko elementy nadrzędne są pobierane podczas początkowego ładowania. Serwery proxy wykrywają, kiedy jest podejmowana próba uzyskania dostępu do jednostek podrzędnych i automatycznie ładuje je na żądanie.
Definiowanie modelu
W tym przewodniku zaimplementujesz model przy użyciu polecenia "code first". Oznacza to, że program EF Core utworzy tabele i schemat bazy danych na podstawie zdefiniowanych klas języka C#.
Dodaj nową klasę. Nadaj jej nazwę: Product.cs
i wypełnij ją następująco:
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; }
}
}
Następnie dodaj klasę o nazwie Category.cs
i wypełnij ją następującym kodem:
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>();
}
}
Właściwość Products w klasie Category i właściwości Category w klasie Product to właściwości nawigacji. W programie Entity Framework właściwości nawigacji zapewniają sposób nawigowania po relacji między dwoma typami jednostek.
Oprócz definiowania jednostek należy zdefiniować klasę pochodzącą z elementu DbContext i uwidaczniać właściwości TEntity< zestawu dbSet>. Właściwości DbSet<TEntity> poinformują kontekst o typach, które mają zostać uwzględnione w modelu.
Wystąpienie typu pochodnego DbContext zarządza obiektami encji podczas działania, co obejmuje wypełnianie obiektów danymi z bazy danych, śledzenie zmian i zapisywanie danych w niej.
Dodaj nową ProductContext.cs
klasę do projektu z następującą definicją:
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();
}
}
}
- Narzędzie
DbSet
informuje platformę EF Core o tym, jakie jednostki języka C# powinny być mapowane na bazę danych. - Istnieje wiele sposobów konfigurowania programu EF Core
DbContext
. Informacje o nich można znaleźć w sekcji Konfigurowanie DbContext. - W tym przykładzie użyto
OnConfiguring
nadpisania w celu określenia pliku danych Sqlite. - Wywołanie
UseLazyLoadingProxies
informuje EF Core o zaimplementowaniu ładowania leniwego, więc obiekty podrzędne są automatycznie ładowane, gdy są uzyskiwane z elementu nadrzędnego.
Naciśnij CTRL+SHIFT+B lub przejdź do Kompiluj > Kompiluj rozwiązanie, aby skompilować projekt.
Wskazówka
Dowiedz się więcej o tym, jak zachować synchronizację baz danych i modeli EF Core: zarządzanie schematami baz danych.
Leniwe ładowanie
Właściwość Products w klasie Category i właściwości Category w klasie Product to właściwości nawigacji. W programie Entity Framework Core właściwości nawigacji zapewniają sposób nawigowania po relacji między dwoma typami jednostek.
Program EF Core umożliwia automatyczne ładowanie powiązanych jednostek z bazy danych przy pierwszym uzyskiwaniu dostępu do właściwości nawigacji. W przypadku tego typu ładowania (nazywanego ładowaniem leniwym) należy pamiętać, że przy pierwszym uzyskiwaniu dostępu do każdej właściwości nawigacji zostanie wykonane oddzielne zapytanie względem bazy danych, jeśli zawartość nie znajduje się jeszcze w kontekście.
W przypadku korzystania z typów jednostek "Plain Old C# Object" (POCO), program EF Core osiąga opóźnione ładowanie, tworząc podczas wykonywania programu wystąpienia pochodnych typów proxy i następnie przesłaniając właściwości wirtualne w Twoich klasach w celu dodania mechanizmu ładowania. Aby uzyskać leniwe ładowanie powiązanych obiektów, właściwości nawigacyjne muszą być zadeklarowane jako publiczne i wirtualne (Overridable w Visual Basic), a klasa nie może być zapieczętowana (NotOverridable w Visual Basic). W przypadku korzystania z Database First właściwości nawigacji są automatycznie tworzone jako wirtualne, aby umożliwić leniwe ładowanie.
Wiązanie obiektu z kontrolkami
Dodaj klasy zdefiniowane w modelu jako źródła danych dla tej aplikacji WPF.
Kliknij dwukrotnie plik MainWindow.xaml w Eksploratorze rozwiązań, aby otworzyć formularz główny
Wybierz kartę XAML , aby edytować kod XAML.
Natychmiast po tagu otwierania
Window
dodaj następujące źródła, aby nawiązać połączenie z jednostkami programu EF Core.<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>
Spowoduje to skonfigurowanie źródła dla kategorii "nadrzędnych" i drugiego źródła dla produktów "detail".
Następnie dodaj następujący znacznik do kodu XAML po otwierającym tagu
Grid
.<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>
Należy pamiętać, że
CategoryId
właściwość jest ustawiona naReadOnly
, ponieważ jest przypisana przez bazę danych i nie można jej zmienić.
Dodawanie siatki szczegółów
Teraz, gdy siatka istnieje do wyświetlania kategorii, można dodać siatkę szczegółów do wyświetlania produktów. Dodaj to wewnątrz elementu Grid
, za elementem kategorii DataGrid
.
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>
Na koniec dodaj Save
przycisk i podłącz go do zdarzenia kliknięcia.Button_Click
<Button Content="Save" HorizontalAlignment="Center" Margin="0,240,0,0"
Click="Button_Click" Height="20" Width="123"/>
Widok projektu powinien wyglądać następująco:
Dodawanie kodu obsługującego interakcję z danymi
Nadszedł czas, aby dodać niektóre programy obsługi zdarzeń do okna głównego.
W oknie XAML kliknij <element Okno> , aby wybrać okno główne.
W oknie Właściwości wybierz Zdarzenia w prawym górnym rogu, a następnie kliknij dwukrotnie pole tekstowe znajdujące się na prawo od etykiety Loaded.
Spowoduje to przejście do kodu związanego z formularzem. Teraz edytujemy kod, aby użyć ProductContext
do wykonywania dostępu do danych. Zaktualizuj kod, jak pokazano poniżej.
Kod deklaruje długotrwałe wystąpienie klasy ProductContext
. Obiekt ProductContext
jest używany do wykonywania zapytań i zapisywania danych w bazie danych. Metoda Dispose()
w wystąpieniu ProductContext
jest następnie wywoływana z przesłoniętej metody OnClosing
. Komentarze kodu wyjaśniają, co robi każdy krok.
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);
}
}
}
Uwaga / Notatka
Kod używa wywołania do EnsureCreated()
zbudowania bazy danych przy pierwszym uruchomieniu. Jest to dopuszczalne w przypadku pokazów, ale w aplikacjach produkcyjnych należy przyjrzeć się migracjom w celu zarządzania schematem. Kod jest również wykonywany synchronicznie, ponieważ używa lokalnej bazy danych SQLite. W przypadku scenariuszy produkcyjnych, które zwykle obejmują serwer zdalny, rozważ użycie asynchronicznych wersji metod Load
i SaveChanges
.
Testowanie aplikacji WPF
Skompiluj i uruchom aplikację, naciskając F5 lub wybierając pozycję Debuguj > rozpocznij debugowanie. Baza danych powinna zostać utworzona automatycznie przy użyciu pliku o nazwie products.db
. Wprowadź nazwę kategorii i naciśnij Enter, a następnie dodaj produkty do dolnej siatki. Kliknij pozycję Zapisz i obejrzyj odświeżanie siatki przy użyciu podanych identyfikatorów bazy danych. Wyróżnij wiersz i naciśnij pozycję Usuń , aby usunąć wiersz. Jednostka zostanie usunięta po kliknięciu przycisku Zapisz.
Powiadomienie o zmianie własności
W tym przykładzie przedstawiono cztery kroki synchronizacji jednostek z interfejsem użytkownika.
- Początkowe wywołanie
_context.Categories.Load()
ładuje dane kategorii. - Leniwe serwery proxy ładują dane produktów zależnych.
- Wbudowane śledzenie zmian platformy EF Core wprowadza niezbędne modyfikacje jednostek, w tym wstawiania i usuwania, gdy
_context.SaveChanges()
jest wywołane. - Wywołania
DataGridView.Items.Refresh()
wymuszają ponowne ładowanie przy użyciu nowo wygenerowanych identyfikatorów.
To działa w przypadku naszego przykładowego projektu startowego, ale w innych scenariuszach może być potrzebny dodatkowy kod. Kontrolki WPF renderują interfejs użytkownika, odczytując pola i właściwości bytów. Podczas edytowania wartości w interfejsie użytkownika ta wartość jest przekazywana do jednostki. Po zmianie wartości właściwości bezpośrednio w jednostce, takiej jak załadowanie jej z bazy danych, platforma WPF nie będzie natychmiast odzwierciedlać zmian w interfejsie użytkownika. Aparat renderowania musi być powiadamiany o zmianach. Projekt wykonał to poprzez ręczne wywołanie Refresh()
. Łatwym sposobem automatyzacji tego powiadomienia jest zaimplementowanie interfejsu INotifyPropertyChanged . Składniki WPF automatycznie wykrywają interfejs i rejestrują się pod kątem zdarzeń zmiany. Jednostka jest odpowiedzialna za zgłaszanie tych zdarzeń.
Wskazówka
Aby dowiedzieć się więcej o sposobie obsługi zmian, przeczytaj: Jak zaimplementować powiadomienie o zmianie właściwości.
Dalsze kroki
Dowiedz się więcej na temat konfigurowania DbContext.