Sdílet prostřednictvím


Začínáme s WPF

Tento podrobný návod ukazuje, jak svázat typy POCO s ovládacími prvky WPF ve formuláři "main-detail". Aplikace používá rozhraní API entity Framework k naplnění objektů daty z databáze, sledování změn a zachování dat do databáze.

Model definuje dva typy, které se účastní relace 1:N: Category (principal\main) a Product (závislý\detail). Architektura datových vazeb WPF umožňuje navigaci mezi souvisejícími objekty: výběr řádků v hlavním zobrazení způsobí, že se zobrazení podrobností aktualizuje s odpovídajícími podřízenými daty.

Snímky obrazovky a výpisy kódu v tomto návodu pocházejí ze sady Visual Studio 2019 16.6.5.

Tip

Ukázku tohoto článku najdete naGitHubu .

Pre-Requisites

K dokončení tohoto návodu musíte mít nainstalovanou sadu Visual Studio 2019 16.3 nebo novější s vybranou úlohou .NET Desktop . Další informace o instalaci nejnovější verze sady Visual Studio naleznete v tématu Instalace sady Visual Studio.

Vytvoření aplikace

  1. Otevřete Visual Studio
  2. V úvodním okně zvolte Vytvořit nový projekt.
  3. Vyhledejte "WPF", zvolte APLIKACI WPF (.NET) a pak zvolte Další.
  4. Na další obrazovce zadejte název projektu, například GetStartedWPF, a zvolte Vytvořit.

Nainstalujte balíčky NuGet Entity Framework

  1. Klikněte pravým tlačítkem na řešení a zvolte Spravovat balíčky NuGet pro řešení...

    Správa balíčků NuGet

  2. Zadejte entityframeworkcore.sqlite do vyhledávacího pole.

  3. Vyberte balíček Microsoft.EntityFrameworkCore.Sqlite .

  4. Zkontrolujte projekt v pravém podokně a klikněte na Nainstalovat.

    Balíček Sqlite

  5. Opakováním kroků vyhledejte entityframeworkcore.proxies a nainstalujte Microsoft.EntityFrameworkCore.Proxies.

Note

Když jste nainstalovali balíček Sqlite, automaticky se stáhl související základní balíček Microsoft.EntityFrameworkCore . Balíček Microsoft.EntityFrameworkCore.Proxies poskytuje podporu pro opožděná načítání dat. To znamená, že pokud máte entity s podřízenými entitami, se při počátečním načtení načítají pouze nadřazené entity. Proxy servery zjistí, kdy se provede pokus o přístup k podřízeným entitám, a automaticky je načte na vyžádání.

Definování modelu

V tomto názorném postupu implementujete model pomocí kódu jako první. To znamená, že EF Core vytvoří databázové tabulky a schéma založené na definovaných třídách jazyka C#.

Přidejte novou třídu. Pojmenujte ho takto: Product.cs a naplňte ho následovně:

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; }
    }
}

Dále přidejte třídu s názvem Category.cs a naplňte ji následujícím kódem:

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>();
    }
}

Vlastnost Products ve třídě Category a Category ve třídě Product jsou navigační vlastnosti. Ve službě Entity Framework poskytují navigační vlastnosti způsob, jak přecházet mezi dvěma typy entit.

Kromě definování entit je nutné definovat třídu, která je odvozena z DbContext a zveřejňuje DbSet<TEntity> vlastnosti. Vlastnosti DbSet<TEntity> znají kontext, které typy chcete zahrnout do modelu.

Instance odvozeného typu DbContext spravuje objekty entity během doby běhu, což zahrnuje naplnění objektů dat z databáze, sledování změn a zachování dat do databáze.

Přidejte do projektu novou ProductContext.cs třídu s následující definicí:

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();
        }
    }
}
  • Informuje DbSet EF Core o tom, jaké entity jazyka C# by měly být mapovány na databázi.
  • Existují různé způsoby konfigurace EF Core DbContext. O nich si můžete přečíst v tématu Konfigurace DbContext.
  • Tento příklad používá přepis OnConfiguring k určení datového souboru SQLite.
  • Volání UseLazyLoadingProxies instruuje EF Core k implementaci líného načítání, takže podřízené entity jsou automaticky načteny při přístupu z nadřazeného objektu.

Stisknutím CTRL+SHIFT+B nebo přejděte na Stavět > Stavět řešení zkompilujte projekt.

Tip

Zjistěte, jakými způsoby můžete udržet databázi a modely EF Core v synchronizaci: Správa schémat databáze.

Líné načítání

Vlastnost Products ve třídě Category a Category ve třídě Product jsou navigační vlastnosti. V Entity Framework Core poskytují navigační vlastnosti způsob, jak procházet vztah mezi dvěma typy entit.

EF Core nabízí možnost načíst související entity z databáze automaticky při prvním přístupu k navigační vlastnosti. U tohoto typu načítání (označovaného jako opožděné načítání) mějte na paměti, že při prvním přístupu ke každé navigační vlastnosti se vůči databázi spustí samostatný dotaz, pokud obsah ještě není v kontextu.

Pokud používáte entity "Plain Old C# Object" (POCO), EF Core dosahuje opožděného načítání vytvořením instancí odvozených typů proxy během běhu a přepsáním virtuálních vlastností ve vašich třídách, aby se přidalo načítání háku. Chcete-li získat opožděné načítání souvisejících objektů, musíte deklarovat navigační vlastnost getters jako veřejné a virtuální (Overridable v jazyce Visual Basic) a vaše třída nesmí být zapečetěná (NotOverridable v jazyce Visual Basic). Při použití funkce Database First se navigační vlastnosti automaticky vytvoří jako virtuální, aby se povolilo opožděné načítání.

Vytvoření vazby objektu k ovládacím prvkům

Přidejte třídy definované v modelu jako zdroje dat pro tuto aplikaci WPF.

  1. Poklikáním na MainWindow.xaml v Průzkumníku řešení otevřete hlavní formulář.

  2. Zvolte kartu XAML a upravte xaml.

  3. Hned za počáteční Window značku přidejte následující zdroje pro připojení k entitě 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>
    
  4. Tímto se nastavuje zdroj pro "rodičovské" kategorie a druhý zdroj pro "podrobné" produkty.

  5. Dále přidejte následující označení do XAML za počáteční Grid značku.

    <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>
    
  6. Všimněte si, že CategoryId je nastaveno na ReadOnly protože je přiřazeno databází a nelze jej změnit.

Přidání mřížky podrobností

Teď, když mřížka existuje pro zobrazení kategorií, je možné přidat mřížku podrobností, aby se zobrazily produkty. Přidejte tento prvek do elementu Grid za element categories 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>

Nakonec přidejte tlačítko Save a propojte událost kliknutí Button_Click.

<Button Content="Save" HorizontalAlignment="Center" Margin="0,240,0,0" 
        Click="Button_Click" Height="20" Width="123"/>

Vaše návrhové zobrazení by mělo vypadat takto:

Snímek obrazovky návrháře WPF

Přidání kódu, který zpracovává interakci s daty

Je čas přidat některé obslužné rutiny událostí do hlavního okna.

  1. V okně XAML klikněte na prvek <Window>, abyste vybrali hlavní okno.

  2. V okně Vlastnosti zvolte Události v pravém horním rohu a potom poklikejte na textové pole napravo od načteného popisku.

    Vlastnosti hlavního okna

Tím se dostanete ke kódu na pozadí pro formulář, který nyní upravíme tak, abychom použili ProductContext k provádění přístupu k datům. Aktualizujte kód, jak je znázorněno níže.

Kód deklaruje dlouhotrvající instanci ProductContext. Objekt ProductContext se používá k dotazování a ukládání dat do databáze. Metoda Dispose() v ProductContext instanci se pak volá z přepsáné OnClosing metody. Komentáře ke kódu vysvětlují, co každý krok dělá.

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);
        }
    }
}

Note

Kód používá volání na EnsureCreated() pro sestavení databáze při prvním spuštění. To je přijatelné pro ukázky, ale v produkčních aplikacích byste se měli podívat na migrace pro správu schématu. Kód se také spouští synchronně, protože používá místní databázi SQLite. V produkčních scénářích, které obvykle zahrnují vzdálený server, zvažte použití asynchronních Load verzí a SaveChanges metod.

Testování aplikace WPF

Zkompilujte a spusťte aplikaci stisknutím klávesy F5 nebo výběrem Spustit > ladění. Databáze by se měla automaticky vytvořit pomocí souboru s názvem products.db. Zadejte název kategorie a stiskněte Enter a pak přidejte produkty do dolní mřížky. Klikněte na Uložit a sledujte aktualizaci mřížky s ID přidělenými databází. Zvýrazněte řádek a stisknutím klávesy Odstranit odeberte řádek. Entita bude odstraněna po kliknutí na tlačítko Uložit.

Spuštění aplikace

Oznámení o změně vlastnosti

Tento příklad využívá čtyři kroky k synchronizaci entit s uživatelským rozhraním.

  1. Počáteční volání _context.Categories.Load() načte data kategorií.
  2. Proxy pro líné načítání načítají data závislých produktů.
  3. Vestavěné sledování změn EF Core provádí potřebné úpravy entit, včetně vkládání a odstraňování, když se volá _context.SaveChanges().
  4. Volání DataGridView.Items.Refresh() vynutí opětovné načtení s nově vygenerovanými ID.

To funguje pro naši ukázku Začínáme, ale pro jiné scénáře můžete vyžadovat další kód. Ovládací prvky WPF vykreslují uživatelské rozhraní čtením polí a vlastností entit. Při úpravě hodnoty v uživatelském rozhraní (UI) se tato hodnota předá vaší entitě. Když změníte hodnotu vlastnosti přímo na entitě, například ji načítáte z databáze, WPF okamžitě neodráží změny v uživatelském rozhraní. Vykreslovací modul musí být informován o změnách. Projekt to udělal ručním voláním Refresh(). Snadný způsob, jak toto oznámení automatizovat, je implementace INotifyPropertyChanged rozhraní. Komponenty WPF automaticky rozpozná rozhraní a zaregistrují události změn. Entita zodpovídá za vyvolání těchto událostí.

Tip

Další informace o tom, jak zpracovávat změny, najdete v tématu : Jak implementovat oznámení o změně vlastnosti.

Další kroky

Přečtěte si další informace o konfiguraci DbContext.