Vazby dat pomocí WPF

Důležité

Tento dokument je platný pouze pro WPF v rozhraní .NET Framework.

Tento dokument popisuje vazbu dat pro WPF v rozhraní .NET Framework. Pro nové projekty .NET Core doporučujeme místo Entity Frameworku 6 použít EF Core . Dokumentace pro datovou vazbu v EF Core je tady: Začínáme s WPF.

Tento podrobný návod ukazuje, jak svázat typy POCO s ovládacími prvky WPF ve formuláři master-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\master) a Product (závislý\detail). Nástroje sady Visual Studio se pak používají k vytvoření vazby typů definovaných v modelu k ovládacím prvkům WPF. 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 2013, ale tento návod můžete dokončit pomocí sady Visual Studio 2012 nebo Visual Studio 2010.

Použití možnosti Objekt pro vytváření zdrojů dat WPF

V předchozí verzi Entity Frameworku jsme při vytváření nového zdroje dat na základě modelu vytvořeného pomocí nástroje EF Designer doporučili použití možnosti Databáze . Důvodem bylo, že návrhář vygeneroval kontext odvozený z ObjectContext a tříd entit odvozených z EntityObject. Použití možnosti Databáze vám pomůže napsat nejlepší kód pro interakci s touto plochou rozhraní API.

Návrháři EF pro Visual Studio 2012 a Visual Studio 2013 generují kontext odvozený z DbContext společně s jednoduchými třídami entit POCO. V sadě Visual Studio 2010 doporučujeme přepnout na šablonu generování kódu, která používá DbContext, jak je popsáno dále v tomto návodu.

Při použití rozhraní API DbContext byste při vytváření nového zdroje dat měli použít možnost Objekt , jak je znázorněno v tomto návodu.

V případě potřeby se můžete vrátit k generování kódu založenému na ObjectContext pro modely vytvořené pomocí EF Designeru.

Předpoklady

K dokončení tohoto návodu musíte mít nainstalovanou sadu Visual Studio 2013, Visual Studio 2012 nebo Visual Studio 2010.

Pokud používáte Visual Studio 2010, musíte také nainstalovat NuGet. Další informace najdete v tématu Instalace NuGetu.  

Vytvoření aplikace

  • Otevřete sadu Visual Studio.
  • Soubor - Nový ->> Project....
  • Výběr Windows v levém podokně a WPFApplication v pravém podokně
  • Jako název zadejte WPFwithEFSample .
  • Vyberte OK.

Instalace balíčku NuGet entity Framework

  • V Průzkumník řešení klikněte pravým tlačítkem myši na projekt WinFormswithEFSample.
  • Vyberte Spravovat balíčky NuGet...
  • V dialogovém okně Spravovat balíčky NuGet vyberte kartu Online a zvolte balíček EntityFramework .
  • Klikněte na Nainstalovat.

    Poznámka

    Kromě sestavení EntityFramework se také přidá odkaz na System.ComponentModel.DataAnnotations. Pokud má projekt odkaz na System.Data.Entity, při instalaci balíčku EntityFramework se odebere. Sestavení System.Data.Entity se už nepoužívá pro aplikace Entity Framework 6.

Definování modelu

V tomto názorném postupu můžete zvolit implementaci modelu pomocí code First nebo EF Designeru. Dokončete jednu ze dvou následujících částí.

Možnost 1: Definování modelu pomocí kódu First

Tato část ukazuje, jak vytvořit model a jeho přidruženou databázi pomocí code First. Přejděte k další části (možnost 2: Definování modelu pomocí databáze First), pokud byste raději použili Database First k zpětnému analýzu modelu z databáze pomocí návrháře EF.

Při použití vývoje Code First obvykle začínáte psaním tříd rozhraní .NET Framework, které definují váš konceptuální (doménový) model.

  • Přidejte novou třídu do WPFwithEFSample:
    • Klikněte pravým tlačítkem myši na název projektu.
    • Vyberte Přidat a pak Novou položku.
    • Vyberte třídu a jako název třídy zadejte Produkt .
  • Nahraďte definici třídy Product následujícím kódem:
    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; }
        }
    }
  • Přidejte třídu Category s následující definicí:
    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; }
        }
    }

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 třídu ProductContext s následující definicí:
    using System.Data.Entity;

    namespace WPFwithEFSample
    {
        public class ProductContext : DbContext
        {
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }

Zkompilujte projekt.

Možnost 2: Definování modelu pomocí database First

Tato část ukazuje, jak pomocí Database First provést zpětnou analýzu modelu z databáze pomocí návrháře EF. Pokud jste dokončili předchozí část (možnost 1: Definování modelu pomocí kódu First), přeskočte tuto část a přejděte přímo do části Opožděné načítání .

Vytvoření existující databáze

Obvykle platí, že když cílíte na existující databázi, bude již vytvořena, ale pro účely tohoto názorného postupu potřebujeme vytvořit databázi pro přístup.

Databázový server nainstalovaný se sadou Visual Studio se liší v závislosti na nainstalované verzi sady Visual Studio:

  • Pokud používáte Visual Studio 2010, budete vytvářet databázi SQL Express.
  • Pokud používáte Visual Studio 2012, budete vytvářet databázi LocalDB .

Pojďme pokračovat a vygenerovat databázi.

  • Zobrazení –> Průzkumník serveru

  • Klikněte pravým tlačítkem na Připojení iony dat –> přidat Připojení ion...

  • Pokud jste se ještě nepřipojili k databázi z Průzkumníka serveru, musíte jako zdroj dat vybrat Microsoft SQL Server.

    Change Data Source

  • Připojení do LocalDB nebo SQL Express v závislosti na tom, který máte nainstalovaný, a zadejte Produkty jako název databáze

    Add Connection LocalDB

    Add Connection Express

  • Vyberte OK a zobrazí se dotaz, jestli chcete vytvořit novou databázi, vyberte Ano.

    Create Database

  • Nová databáze se teď zobrazí v Průzkumníku serveru, klikněte na ni pravým tlačítkem myši a vyberte Nový dotaz.

  • Zkopírujte následující SQL do nového dotazu, klikněte pravým tlačítkem myši na dotaz a vyberte Spustit.

    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

Model zpětné analýzy

K vytvoření modelu použijeme Návrhář entity Framework, který je součástí sady Visual Studio.

  • Projekt –> přidat novou položku...

  • V nabídce vlevo vyberte Data a pak ADO.NET Entity Data Model.

  • Jako název zadejte ProductModel a klikněte na OK.

  • Tím se spustí Průvodce datovým modelem entity.

  • Vyberte Vygenerovat z databáze a klikněte na Další.

    Choose Model Contents

  • Vyberte připojení k databázi, kterou jste vytvořili v první části, jako název připojovací řetězec zadejte ProductContext a klikněte na Další.

    Choose Your Connection

  • Kliknutím na zaškrtávací políčko vedle tabulky naimportujete všechny tabulky a kliknete na Dokončit.

    Choose Your Objects

Jakmile proces zpětné analýzy dokončí nový model, přidá se do projektu a otevře se vám zobrazení v Návrháři entity Framework. Do projektu byl přidán také soubor App.config s podrobnostmi o připojení pro databázi.

Další kroky v sadě Visual Studio 2010

Pokud pracujete v sadě Visual Studio 2010, budete muset aktualizovat návrháře EF tak, aby používal generování kódu EF6.

  • Klikněte pravým tlačítkem na prázdné místo modelu v nástroji EF Designer a vyberte Přidat položku generování kódu...
  • V nabídce vlevo vyberte online šablony a vyhledejte DbContext.
  • Vyberte generátor EF 6.x DbContext pro jazyk C#, jako název zadejte ProductsModel a klikněte na Přidat.

Aktualizace generování kódu pro datová vazba

EF vygeneruje kód z modelu pomocí šablon T4. Šablony dodávané se sadou Visual Studio nebo stažené z galerie sady Visual Studio jsou určené pro obecné účely. To znamená, že entity generované z těchto šablon mají jednoduché ICollection<T> vlastnosti. Při vytváření datových vazeb pomocí WPF je však žádoucí použít ObservableCollection pro vlastnosti kolekce, aby WPF mohl sledovat změny provedené v kolekcích. K tomuto účelu upravíme šablony tak, aby používaly ObservableCollection.

  • Otevřete Průzkumník řešení a najděte soubor ProductModel.edmx.

  • Vyhledejte soubor ProductModel.tt, který bude vnořený pod souborem ProductModel.edmx.

    WPF Product Model Template

  • Poklikáním otevřete soubor ProductModel.tt v editoru sady Visual Studio.

  • Vyhledejte a nahraďte dva výskyty "ICollection" slovem "ObservableCollection". Nachází se přibližně na řádcích 296 a 484.

  • Vyhledejte a nahraďte první výskyt "HashSet" "ObservableCollection". Tento výskyt se nachází přibližně na řádku 50. Nenahrazovat druhý výskyt HashSet nalezen později v kódu.

  • Vyhledejte a nahraďte jediný výskyt "System.Collections.Generic" "System.Collections.ObjectModel". Nachází se přibližně na řádku 424.

  • Uložte soubor ProductModel.tt. To by mělo způsobit opětovné vygenerování kódu entit. Pokud se kód automaticky nevygeneruje, klikněte pravým tlačítkem na ProductModel.tt a zvolte Spustit vlastní nástroj.

Pokud teď otevřete soubor Category.cs (který je vnořený pod ProductModel.tt), měli byste vidět, že kolekce Products má typ ObservableCollection<Product>.

Zkompilujte projekt.

Opožděné načítání

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.

EF 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.

Při použití typů entit POCO ef dosahuje opožděného načítání vytvořením instancí odvozených typů proxy během běhu a následným přepsáním virtuálních vlastností ve vašich třídách, aby se přidala zátěžová háka. 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í vlastností navigace Database First se automaticky vytvoří virtuální, aby se povolilo opožděné načítání. V části Code First jsme se rozhodli nastavit, aby navigační vlastnosti byly virtuální z stejného důvodu.

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

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

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

  • V hlavní nabídce vyberte Projekt –> Přidat nový zdroj dat ... (v sadě Visual Studio 2010 musíte vybrat Data –> Přidat nový zdroj dat...)

  • V okně Zvolit typ zdroje dat vyberte objekt a klikněte na Další.

  • V dialogovém okně Vybrat datové objekty rozbalte WPFwithEFSample dvakrát a vyberte Kategorie.
    Není nutné vybrat zdroj dat Product , protože se k němu dostaneme prostřednictvím vlastnosti Produktu ve zdroji dat Kategorie .

    Select Data Objects

  • Klikněte na Finish (Dokončit).

  • Okno Zdroje dat se otevře vedle okna MainWindow.xaml Pokud se okno Zdroje dat nezobrazuje, vyberte Zobrazit –> Jiné zdroje dat Windows.>

  • Stiskněte ikonu připnutí, takže okno Zdroje dat se automaticky neskryje. Možná budete muset stisknout tlačítko aktualizovat, pokud už bylo okno viditelné.

    Data Sources

  • Vyberte zdroj dat Kategorie a přetáhněte ho ve formuláři.

Při přetažení tohoto zdroje došlo k následujícímu:

  • Prostředek categoryViewSource a ovládací prvek categoryDataGrid byly přidány do XAML.
  • Vlastnost DataContext nadřazeného prvku mřížky byla nastavena na {StaticResource categoryViewSource }. Prostředek categoryViewSource slouží jako zdroj vazby pro vnější\nadřazený element Grid. Vnitřní prvky mřížky pak dědí hodnotu DataContext z nadřazené mřížky (vlastnost ItemsSource categoryDataGrid je nastavena na {Binding}).
    <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>

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

Teď, když máme mřížku pro zobrazení kategorií, přidáme mřížku podrobností pro zobrazení přidružených produktů.

  • Vyberte vlastnost Products ze zdroje dat Kategorie a přetáhněte ji ve formuláři.
    • Do XAML se přidá prostředek categoryProductsViewSource a mřížka productDataGrid .
    • Cesta vazby pro tento prostředek je nastavená na Produkty.
    • Architektura datové vazby WPF zajišťuje, že se v productDataGridu zobrazí pouze produkty související s vybranou kategorií.
  • Z panelu nástrojů přetáhněte tlačítko na formulář. Nastavte vlastnost Name na buttonSave a Content vlastnost Uložit.

Formulář by měl vypadat nějak takto:

Designer Form

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.

  • V okně XAML klikněte na <prvek Okno . Tím se vybere hlavní okno.

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

    Main Window Properties

  • Přidejte také událost Kliknutí pro tlačítko Uložit poklikáním na tlačítko Uložit v návrháři.

Tím se dostanete na kód formuláře, který teď upravíme tak, aby k provádění přístupu k datům používal ProductContext. Aktualizujte kód pro MainWindow, jak je znázorněno níže.

Kód deklaruje dlouho běžící instanci ProductContext. ProductContext objekt slouží k dotazování a ukládání dat do databáze. Dispose () u instance ProductContext je pak volána z přepsání OnClosing metoda. Komentáře ke kódu poskytují podrobnosti o tom, co kód dělá.

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

    }

Testování aplikace WPF

  • Zkompilujte a spusťte aplikaci. Pokud jste použili Code First, uvidíte, že se pro vás vytvoří databáze WPFwithEFSample.ProductContext .

  • Do horní mřížky zadejte název kategorie a názvy produktů v dolní mřížce Nezadávejte nic do sloupců ID, protože databáze generuje primární klíč.

    Main Window with new categories and products

  • Stisknutím tlačítka Uložit uložte data do databáze.

Po volání SaveChanges()DbContext se ID naplní hodnotami vygenerovanými databází. Vzhledem k tomu, že jsme po SaveChanges() ovládací prvky DataGrid aktualizovali také o nové hodnoty.

Main Window with IDs populated

Další zdroje informací

Další informace o datových vazbách ke kolekcím pomocí WPF najdete v tomto tématu v dokumentaci WPF.