Vytvoření základní datové aplikace pomocí WPF a Entity Frameworku 6

Poznámka:

Pokud používáte Visual Studio 2022, ujistěte se, že pro účely tohoto kurzu používáte verzi 17.3 nebo novější.

V tomto kurzu se dozvíte, jak vytvořit základní formuláře pro datovou aplikaci v sadě Visual Studio. Aplikace používá SQL Server LocalDB, databázi Northwind, Entity Framework 6 (nikoli Entity Framework Core) a Windows Presentation Foundation (WPF) pro rozhraní .NET Framework (nikoli .NET Core nebo .NET 5 nebo novější). Ukazuje, jak provádět základní datové vazby pomocí zobrazení podrobností předlohy a obsahuje vlastní ovládací prvek BindingNavigator s tlačítky, která dělají toto: přechod na první, přesunutí na předchozí, přesunutí na další, přesunutí na poslední, odstranění, přidání, nastavení nového pořadí, aktualizaci a zrušení.

Tento kurz se zaměřuje na použití datových nástrojů v sadě Visual Studio a nepokouší se vysvětlit základní technologie do hloubky. Předpokládá se, že máte základní znalosti jazyka XAML (Extensible Application Markup Language), Entity Framework a SQL. I když kód v tomto kurzu neukazuje architekturu modeluView-ViewModel (MVVM), která je standardní pro aplikace WPF, můžete kód zkopírovat několika úpravami do vlastní aplikace MVVM.

Pokud chcete zobrazit konečný kód pro tento kurz, podívejte se na ukázky kurzů k sadě Visual Studio – EF6.

V tomto kurzu se naučíte:

  • Instalace a připojení k Northwind
  • Konfigurace projektu aplikace WPF
  • Vytvoření datového modelu entity ADO.NET
  • Připojte data modelu k stránce XAML.
  • Úprava návrhu stránky a přidání mřížky
  • Přidání tlačítek pro navigaci, přidání, aktualizaci a odstranění
  • Spuštění aplikace WPF

Požadavky

  • Visual Studio s nainstalovaným pracovním vytížením Vývoj pro .NET Desktop a s nainstalovanou komponentou Windows Communication Foundation Instalace:

    1. Otevřete aplikaci Instalační program sady Visual Studio nebo v nabídce Visual Studio vyberte Nástroje> a Získat nástroje a funkce.
    2. V instalačním programu sady Visual Studio zvolte Upravit vedle verze sady Visual Studio, kterou chcete upravit.
    3. Vyberte kartu Jednotlivé komponenty a pak v části Aktivity vývoje zvolte Windows Communication Foundation.
    4. Vyberte Upravit.
  • SQL Server Express LocalDB. Pokud nemáte SQL Server Express LocalDB, můžete jej nainstalovat ze stránky ke stažení SQL Serveru . Nebo ji můžete nainstalovat pomocí aplikace Instalační program sady Visual Studio jako jednotlivé komponenty.

  • Průzkumník objektů SQL Serveru. Pokud ji chcete nainstalovat, nainstalujte úlohu Ukládání a zpracování dat v aplikaci Instalační program sady Visual Studio .

  • Nástroje Entity Framework 6 Obvykle se instaluje při instalaci úlohy Vývoj pro .NET Dektop .

Instalace a připojení k Northwind

Následující příklad používá SQL Server Express LocalDB a ukázkovou databázi Northwind. Pokud ADO.NET poskytovatel dat pro tento produkt podporuje Entity Framework, měl by fungovat i s dalšími databázovými produkty SQL.

Následujícím postupem nainstalujte ukázkovou databázi Northwind:

  1. V sadě Visual Studio otevřete okno Průzkumníka objektů SQL Serveru z nabídky Zobrazit . Rozbalte uzel SQL Serveru . Klikněte pravým tlačítkem na instanci LocalDB a vyberte Nový dotaz.

    Otevře se okno editoru dotazů.

  2. Zkopírujte skript Northwind Transact-SQL (T-SQL) do své schránky.

  3. Vložte skript T-SQL do editoru dotazů a pak zvolte Spustit.

    Dotaz skriptu T-SQL vytvoří databázi Northwind a naplní ji daty.

  4. Přidejte nová připojení pro databázi Northwind.

Konfigurace projektu aplikace WPF

Pokud chcete nakonfigurovat projekt aplikace WPF, postupujte takto:

  1. V sadě Visual Studio vytvořte nový projekt aplikace WPF (.NET Framework) C#.

  2. Přidejte balíček NuGet pro Entity Framework 6. V Průzkumníku řešenívyberte uzel projektu. V hlavní nabídce zvolte Project>Spravovat balíčky NuGet.

  3. Ve Správci balíčků NuGet vyberte odkaz Procházet . Vyhledejte a vyberte balíček EntityFramework . V pravém podokně vyberte Nainstalovat a postupujte podle pokynů.

    Okno Výstup zobrazí průběh a po dokončení instalace vás upozorní.

    Snímek obrazovky znázorňující balíček NuGet Entity Framework

Teď můžete pomocí sady Visual Studio vytvořit model založený na databázi Northwind.

Vytvoření datového modelu entity ADO.NET

Pokud chcete vytvořit datový model entity ADO.NET, postupujte takto:

  1. V Průzkumníku řešení klikněte pravým tlačítkem myši na uzel projektu aplikace WPF a zvolte Přidat>novou položku. V levém podokně pod uzlem C# zvolte Data a v prostředním podokně zvolte ADO.NET Entity Data Model.

    Snímek obrazovky znázorňující okno Přidat novou položku s vybranou možností ADO.NET Entity Data Model

  2. Jako název zadejte Northwind_model a pak zvolte Přidat.

  3. V Průvodci modelem Entity Data Model zvolte EF Designer z databáze a pak vyberte Další.

    Snímek obrazovky znázorňující nástroj EF Designer z databáze vybrané v Průvodci datovým modelem entity

  4. V části Zvolte datové připojení vyberte připojení LocalDB Northwind (například (localdb)\MSSQLLocalDB) a pak vyberte Další.

  5. Pokud se připojení nezobrazuje:

    1. Zvolte Nové připojení. Pokud microsoft SQL Server není vybrán jako zdroj dat v dialogovém okně Vlastnosti připojení , vyberte Změnit. V dialogovém okně Zvolit zdroj dat zvolte Microsoft SQL Server a pak vyberte OK.

    2. V dialogovém okně Vlastnosti připojení zadejte (localdb)\MSSQLLocalDB jako název serveru.

    3. V části Vyberte nebo zadejte název databáze, vyberte Northwind a pak vyberte OK.

    4. V části Zvolit datové připojení vyberte připojení LocalDB Northwind a vyberte Další.

  6. Pokud se zobrazí výzva, zvolte verzi entity Framework, kterou používáte, a pak vyberte Další.

    Snímek obrazovky znázorňující volby verzí pro Entity Framework

  7. Na další stránce průvodce vyberte tabulky, uložené procedury a další databázové objekty, které chcete zahrnout do modelu Entity Framework. Rozbalte uzel dbo pod uzlem Tabulky ve stromovém zobrazení. Vyberte Zákazníci, Podrobnosti objednávky a Objednávky. Ponechte zaškrtnuté výchozí hodnoty a vyberte Dokončit.

    Snímek obrazovky znázorňující vybrané databázové objekty datového modelu

    Průvodce vygeneruje třídy jazyka C#, které představují model Entity Framework, a jsou to data sady Visual Studio svázané s uživatelským rozhraním WPF. V projektu se vytvoří následující soubory:

    • Soubor .edmx popisuje relace a další metadata, která přidružují třídy k objektům v databázi.

    • Soubory .tt jsou šablony T4, které generují kód, který pracuje s modelem, a uloží změny do databáze.

    Tyto soubory jsou viditelné v Průzkumníku řešení pod uzlem Northwind_model :

    Snímek obrazovky znázorňující soubory modelu Entity Framework v Průzkumníku řešení

    Návrhář formulářů pro .edmx soubor se v tomto kurzu nepoužívá, ale můžete ho použít k úpravě určitých vlastností a relací v modelu.

Soubory .tt jsou pro obecné účely a je nutné je upravit, aby fungovaly s datovou vazbou WPF, což vyžaduje ObservableCollection objekty. Postupujte takto:

  1. V Průzkumníku řešení rozbalte uzel Northwind_model , dokud nenajdete Northwind_model.tt. Poklikejte na tento soubor a proveďte následující úpravy:

  2. Stisknutím klávesy F5 sestavte a spusťte projekt. Při prvním spuštění aplikace jsou třídy modelu viditelné v průvodci zdroji dat.

Teď jste připraveni připojit tento model na stránku XAML, abyste mohli zobrazit, procházet a upravovat data.

Připojte data modelu k stránce XAML.

I když je možné napsat vlastní kód datové vazby, je jednodušší nechat Visual Studio, aby ho udělal za vás. Postupujte takto:

  1. V hlavní nabídce zvolte Project>Přidat nový zdroj dat, aby se zobrazil Průvodce konfigurací zdroje dat. Vzhledem k tomu, že jste vázáni na třídy modelu, ne s databází, zvolte Object. Vyberte Další.

    Snímek obrazovky znázorňující Průvodce konfigurací zdroje dat s objektem vybraným jako zdrojem dat

  2. Rozbalte uzel projektu, vyberte objekt Zákazník a pak vyberte Dokončit. Zdroje pro objekty Order jsou automaticky generovány z navigační vlastnosti Objednávky v Customer.

    Snímek obrazovky znázorňující objekt Zákazník vybraný jako zdroj dat

  3. V Průzkumníku řešení poklikejte na MainWindow.xaml v projektu a upravte XAML. Změňte Title z MainWindow na něco přehlednějšího a zvyšte Height a Width na 600 a 800 (tyto hodnoty můžete později změnit, pokud to bude nutné).

  4. Přidejte tyto tři definice řádků do hlavní mřížky, jeden řádek pro navigační tlačítka, jednu pro podrobnosti zákazníka a jednu pro mřížku, která zobrazuje jejich objednávky:

        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    

Dále zobrazíte každou vlastnost třídy v samostatném textovém Customers poli. Postupujte takto:

  1. V Průzkumníku řešení poklikáním na MainWindow.xaml ho otevřete v návrháři.

    Karta Zdroje dat se zobrazí v levém podokně sady Visual Studio poblíž sady nástrojů.

  2. Pokud chcete otevřít okno Zdroje dat, vyberte kartu Zdroje dat nebo v nabídce zvolte Zobrazit> dalšízdroje dat>.

  3. Ve zdrojích dat vyberte Zákazníci a pak v rozevíracím seznamu vyberte Podrobnosti .

  4. Přetáhněte uzel na střední řádek oblasti návrhu. Pokud ho chybně zadáte, můžete později řádek zadat ručně v XAML výběrem Grid.Row="1".

    Ve výchozím nastavení jsou ovládací prvky umístěny svisle v prvku mřížky, ale můžete je uspořádat podle svých potřeb na formuláři. Můžete například umístit textové pole Název nad adresu. Ukázková aplikace pro tento kurz změní pořadí polí a změní jejich uspořádání do dvou sloupců.

    Snímek obrazovky znázorňující vazbu zdroje dat 'Customers' na jednotlivé ovládací prvky.

    V zobrazení XAML teď uvidíte nový Grid prvek v řádku 1 (prostřední řádek) nadřazené mřížky. Nadřazená mřížka má DataContext atribut, který odkazuje na CollectionViewSource prvek, který patří k Windows.Resources elementu. Vzhledem k kontextu dat, když první textové pole naváže na Address, tento název je namapován na Address vlastnost v aktuálním Customer objektu v CollectionViewSource.

    <Grid DataContext="{StaticResource customerViewSource}">
    
  5. OrderPřetáhněte vlastnost objektu Customers třídy do dolní poloviny formuláře, aby ho návrhář umístí do řádku 2.

    Když je zákazník viditelný v horní polovině formuláře, chcete zobrazit objednávky v dolní polovině. Objednávky zobrazíte v jednom ovládacím prvku zobrazení mřížky. Aby datové vazby master-detail fungovaly podle očekávání, je důležité vytvořit vazbu na Orders vlastnost ve Customers třídě, nikoli na samostatný Orders uzel.

    Snímek obrazovky znázorňující třídy Objednávky přetažené a umístěné jako mřížku.

    Visual Studio teď vygeneruje veškerý kód vazby, který připojí ovládací prvky uživatelského rozhraní k událostem v modelu.

  6. Pokud chcete zobrazit nějaká data, napište kód pro naplnění modelu. Přejděte do MainWindow.xaml.cs a přidejte datového člena do třídy MainWindow pro kontext dat.

    Tento objekt, který byl vygenerován za vás, funguje jako ovládací prvek, který sleduje změny a události v modelu.

  7. Přidejte CollectionViewSource datové členy pro zákazníky a objednávky a přidruženou logiku inicializace konstruktoru do existujícího konstruktoru MainWindow(). První část třídy by měla vypadat takto:

    public partial class MainWindow : Window
    {
        NorthwindEntities context = new NorthwindEntities();
        CollectionViewSource custViewSource;
        CollectionViewSource ordViewSource;
    
        public MainWindow()
        {
            InitializeComponent();
            custViewSource = ((CollectionViewSource)(FindResource("customerViewSource")));
            ordViewSource = ((CollectionViewSource)(FindResource("customerOrdersViewSource")));
            DataContext = this;
        }
    
  8. Pokud neexistuje, přidejte direktivu using pro System.Data.Entity aby se metoda Load rozšíření dostala do oboru:

    using System.Data.Entity;
    
  9. Posuňte se dolů a vyhledejte obslužnou rutinu Window_Loaded události. Všimněte si, že visual Studio přidalo CollectionViewSource objekt. Tento objekt představuje NorthwindEntities objekt, který jste vybrali při vytváření modelu. Protože jste ho už přidali, nepotřebujete ho tady. Nahraďte kód Window_Loaded tak, aby metoda vypadala takto:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // 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.Customers.Load();
    
        // After the data is loaded, call the DbSet<T>.Local property    
        // to use the DbSet<T> as a binding source.   
        custViewSource.Source = context.Customers.Local;
    }
    
  10. Stiskněte F5.

    Měli byste vidět podrobnosti o prvním zákazníkovi, který byl načten do systému CollectionViewSource, a jejich objednávky v datové mřížce. Formátování opravíte v další části. Můžete také vytvořit způsob, jak zobrazit ostatní záznamy a provádět základní operace vytváření, čtení, aktualizace a odstraňování (CRUD).

Úprava návrhu stránky a přidání mřížek pro nové zákazníky a objednávky

Výchozí uspořádání vytvořené sadou Visual Studio není ideální pro vaši aplikaci, proto zde poskytneme konečný KÓD XAML, který se má zkopírovat do kódu. Potřebujete také některé mřížky, které uživateli umožní přidat nového zákazníka nebo objednávku.

Pokud chcete přidat nového zákazníka a objednávku, vytvořte samostatnou sadu textových polí, která nejsou svázaná s daty CollectionViewSource. Určujete, kterou mřížku uživatel v libovolném okamžiku uvidí, při nastavení vlastnosti Visible v metodách obslužné rutiny. Nakonec na každý řádek v mřížce Objednávky přidáte tlačítko Odstranit, které uživateli umožní odstranit jednotlivé objednávky.

  1. Otevřete MainWindow.xaml a přidejte do elementu Windows.Resources následující styly:

    <Style x:Key="Label" TargetType="{x:Type Label}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Left"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="23"/>
    </Style>
    <Style x:Key="CustTextBox" TargetType="{x:Type TextBox}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Right"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="26"/>
       <Setter Property="Width" Value="120"/>
    </Style>
    
  2. Nahraďte celou vnější mřížku tímto kódem:

    <Grid>
      <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid x:Name="existingCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" Margin="5" Visibility="Visible" VerticalAlignment="Top" Background="AntiqueWhite" DataContext="{StaticResource customerViewSource}">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/>
          <TextBox x:Name="addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/>
          <TextBox x:Name="postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <Grid x:Name="newCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=newCustomer, UpdateSourceTrigger=Explicit}" Visibility="Collapsed" Background="CornflowerBlue">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true }"/>
          <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <Grid x:Name="newOrderGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding Path=newOrder, Mode=TwoWay}" Visibility="Collapsed" Background="LightGreen">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="New Order Form" FontWeight="Bold"/>
          <Label Content="Employee ID:"  Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_employeeIDTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding EmployeeID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Order Date:"  Grid.Row="2" Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_orderDatePicker" Grid.Row="2"  HorizontalAlignment="Right" Width="120"
                  SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Required Date:" Grid.Row="3" Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_requiredDatePicker" Grid.Row="3" HorizontalAlignment="Right" Width="120"
                   SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Shipped Date:"  Grid.Row="4"  Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_shippedDatePicker"  Grid.Row="4"  HorizontalAlignment="Right" Width="120"
                  SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Ship Via:"  Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_ShipViaTextBox"  Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding ShipVia, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Freight"  Grid.Row="6" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_freightTextBox" Grid.Row="6" Style="{StaticResource CustTextBox}"
                   Text="{Binding Freight, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <DataGrid x:Name="ordersDataGrid" SelectionUnit="Cell" SelectionMode="Single" AutoGenerateColumns="False" CanUserAddRows="false" IsEnabled="True" EnableRowVirtualization="True" Width="auto" ItemsSource="{Binding Source={StaticResource customerOrdersViewSource}}" Margin="10,10,10,10" Grid.Row="2" RowDetailsVisibilityMode="VisibleWhenSelected">
          <DataGrid.Columns>
              <DataGridTemplateColumn>
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <Button Content="Delete" Command="{StaticResource DeleteOrderCommand}" CommandParameter="{Binding}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding CustomerID}" Header="Customer ID" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="employeeIDColumn" Binding="{Binding EmployeeID}" Header="Employee ID" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="freightColumn" Binding="{Binding Freight}" Header="Freight" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="orderDateColumn" Header="Order Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="orderIDColumn" Binding="{Binding OrderID}" Header="Order ID" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="requiredDateColumn" Header="Required Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="shipAddressColumn" Binding="{Binding ShipAddress}" Header="Ship Address" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipCityColumn" Binding="{Binding ShipCity}" Header="Ship City" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipCountryColumn" Binding="{Binding ShipCountry}" Header="Ship Country" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipNameColumn" Binding="{Binding ShipName}" Header="Ship Name" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="shippedDateColumn" Header="Shipped Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="shipPostalCodeColumn" Binding="{Binding ShipPostalCode}" Header="Ship Postal Code" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipRegionColumn" Binding="{Binding ShipRegion}" Header="Ship Region" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipViaColumn" Binding="{Binding ShipVia}" Header="Ship Via" Width="SizeToHeader"/>
          </DataGrid.Columns>
       </DataGrid>
    </Grid>
    

Přidání tlačítek pro navigaci, přidání, aktualizaci a odstranění

V aplikaci Windows Forms máte k dispozici BindingNavigator objekt s tlačítky pro procházení řádků v databázi a provádění základních operací CRUD. I když WPF neposkytuje BindingNavigator, je snadné ho vytvořit vytvořením tlačítek uvnitř vodorovného StackPanela přidružením tlačítek k příkazům, které jsou svázané s metodami v souboru kódu.

Logika příkazu má čtyři části:

  • Příkazy
  • Vazby
  • Tlačítka
  • Zpracovatelé příkazů v kódu

Přidání příkazů, vazeb a tlačítek v XAML

  1. MainWindow.xaml Do souboru přidejte příkazy uvnitř elementu Windows.Resources následujícím způsobem:

    <RoutedUICommand x:Key="FirstCommand" Text="First"/>
    <RoutedUICommand x:Key="LastCommand" Text="Last"/>
    <RoutedUICommand x:Key="NextCommand" Text="Next"/>
    <RoutedUICommand x:Key="PreviousCommand" Text="Previous"/>
    <RoutedUICommand x:Key="DeleteCustomerCommand" Text="Delete Customer"/>
    <RoutedUICommand x:Key="DeleteOrderCommand" Text="Delete Order"/>
    <RoutedUICommand x:Key="UpdateCommand" Text="Update"/>
    <RoutedUICommand x:Key="AddCommand" Text="Add"/>
    <RoutedUICommand x:Key="CancelCommand" Text="Cancel"/>
    
  2. CommandBinding mapuje RoutedUICommand událost na metodu v zákulisí kódu. Přidejte tento CommandBindings prvek za Windows.Resources uzavírací značku takto:

    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource FirstCommand}" Executed="FirstCommandHandler"/>
        <CommandBinding Command="{StaticResource LastCommand}" Executed="LastCommandHandler"/>
        <CommandBinding Command="{StaticResource NextCommand}" Executed="NextCommandHandler"/>
        <CommandBinding Command="{StaticResource PreviousCommand}" Executed="PreviousCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteCustomerCommand}" Executed="DeleteCustomerCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteOrderCommand}" Executed="DeleteOrderCommandHandler"/>
        <CommandBinding Command="{StaticResource UpdateCommand}" Executed="UpdateCommandHandler"/>
        <CommandBinding Command="{StaticResource AddCommand}" Executed="AddCommandHandler"/>
        <CommandBinding Command="{StaticResource CancelCommand}" Executed="CancelCommandHandler"/>
    </Window.CommandBindings>
    
  3. StackPanel Přidejte tlačítka navigace, přidejte, odstraňte a aktualizujte. Přidejte tento styl do Windows.Resources:

    <Style x:Key="NavButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
        <Setter Property="FontSize" Value="24"/>
        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
        <Setter Property="Margin" Value="2,2,2,0"/>
        <Setter Property="Width" Value="40"/>
        <Setter Property="Height" Value="auto"/>
    </Style>
    
  4. Vložte tento kód těsně za RowDefinitions vnější Grid prvek směrem k horní části stránky XAML:

     <StackPanel Orientation="Horizontal" Margin="2,2,2,0" Height="36" VerticalAlignment="Top" Background="Gainsboro" DataContext="{StaticResource customerViewSource}" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin">
         <Button Name="btnFirst" Content="|◄" Command="{StaticResource FirstCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnPrev" Content="◄" Command="{StaticResource PreviousCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnNext" Content="►" Command="{StaticResource NextCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnLast" Content="►|" Command="{StaticResource LastCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnDelete" Content="Delete Customer" Command="{StaticResource DeleteCustomerCommand}" FontSize="11" Width="120" Style="{StaticResource NavButton}"/>
         <Button Name="btnAdd" Content="New Customer" Command="{StaticResource AddCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="New Order" Name="btnNewOrder" FontSize="11" Width="80" Style="{StaticResource NavButton}" Click="NewOrder_click"/>
         <Button Name="btnUpdate" Content="Commit" Command="{StaticResource UpdateCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="Cancel" Name="btnCancel" Command="{StaticResource CancelCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
     </StackPanel>
    

Přidání zpracovatelů příkazů do třídy MainWindow

Kód na pozadí uživatelského rozhraní v MainWindow.xaml.cs je minimální, kromě metod add a delete:

  1. Chcete-li přejít, volejte metody na View vlastnosti CollectionViewSource.

  2. Chcete-li provést kaskádové odstranění v pořadí, použijte vzorový DeleteOrderCommandHandler kód, jak je znázorněno v ukázce kódu. Musíte však nejprve odstranit přidruženou Order_Detailsobjednávku .

  3. UpdateCommandHandler Použijte možnost přidat zákazníka nebo objednávku do kolekce nebo aktualizovat existujícího zákazníka nebo objednávku pomocí změn, které uživatel provede v textových polích.

  4. Přidejte tyto metody obslužné rutiny do třídy MainWindow v MainWindow.xaml.cs. Pokud má vaše tabulka CollectionViewSource Zákazníci jiný název, je nutné upravit tento název ve všech metodách:

    private void LastCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToLast();
    }
    
    private void PreviousCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToPrevious();
    }
    
    private void NextCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToNext();
    }
    
    private void FirstCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToFirst();
    }
    
    private void DeleteCustomerCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        // If existing window is visible, delete the customer and all their orders.  
        // In a real application, you should add warnings and allow the user to cancel the operation.  
        var cur = custViewSource.View.CurrentItem as Customer;
    
        var cust = (from c in context.Customers
                    where c.CustomerID == cur.CustomerID
                    select c).FirstOrDefault();
    
        if (cust != null)
        {
            foreach (var ord in cust.Orders.ToList())
            {
                Delete_Order(ord);
            }
            context.Customers.Remove(cust);
        }
        context.SaveChanges();
        custViewSource.View.Refresh();
    }
    
    // Commit changes from the new customer form, the new order form,  
    // or edits made to the existing customer form.  
    private void UpdateCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        if (newCustomerGrid.IsVisible)
        {
            // Create a new object because the old one  
            // is being tracked by EF now.  
            Customer newCustomer = new Customer
            {
                Address = add_addressTextBox.Text,
                City = add_cityTextBox.Text,
                CompanyName = add_companyNameTextBox.Text,
                ContactName = add_contactNameTextBox.Text,
                ContactTitle = add_contactTitleTextBox.Text,
                Country = add_countryTextBox.Text,
                CustomerID = add_customerIDTextBox.Text,
                Fax = add_faxTextBox.Text,
                Phone = add_phoneTextBox.Text,
                PostalCode = add_postalCodeTextBox.Text,
                Region = add_regionTextBox.Text
            };
    
            // Perform very basic validation  
            if (newCustomer.CustomerID.Length == 5)
            {
                // Insert the new customer at correct position:  
                int len = context.Customers.Local.Count();
                int pos = len;
                for (int i = 0; i < len; ++i)
                {
                    if (String.CompareOrdinal(newCustomer.CustomerID, context.Customers.Local[i].CustomerID) < 0)
                    {
                        pos = i;
                        break;
                    }
                }
                context.Customers.Local.Insert(pos, newCustomer);
                custViewSource.View.Refresh();
                custViewSource.View.MoveCurrentTo(newCustomer);
            }
            else
            {
                MessageBox.Show("CustomerID must have 5 characters.");
            }
    
            newCustomerGrid.Visibility = Visibility.Collapsed;
            existingCustomerGrid.Visibility = Visibility.Visible;
        }
        else if (newOrderGrid.IsVisible)
        {
            // Order ID is auto-generated so we don't set it here.  
            // For CustomerID, address, etc we use the values from current customer.  
            // User can modify these in the datagrid after the order is entered.  
    
            Customer currentCustomer = (Customer)custViewSource.View.CurrentItem;
    
            Order newOrder = new Order()
            {
                OrderDate = add_orderDatePicker.SelectedDate,
                RequiredDate = add_requiredDatePicker.SelectedDate,
                ShippedDate = add_shippedDatePicker.SelectedDate,
                CustomerID = currentCustomer.CustomerID,
                ShipAddress = currentCustomer.Address,
                ShipCity = currentCustomer.City,
                ShipCountry = currentCustomer.Country,
                ShipName = currentCustomer.CompanyName,
                ShipPostalCode = currentCustomer.PostalCode,
                ShipRegion = currentCustomer.Region
            };
    
            try
            {
                newOrder.EmployeeID = Int32.Parse(add_employeeIDTextBox.Text);
            }
            catch
            {
                MessageBox.Show("EmployeeID must be a valid integer value.");
                return;
            }
    
            try
            {
                // Exercise for the reader if you are using Northwind:  
                // Add the Northwind Shippers table to the model.
                
                // Acceptable ShipperID values are 1, 2, or 3.  
                if (add_ShipViaTextBox.Text == "1" || add_ShipViaTextBox.Text == "2"
                    || add_ShipViaTextBox.Text == "3")
                {
                    newOrder.ShipVia = Convert.ToInt32(add_ShipViaTextBox.Text);
                }
                else
                {
                    MessageBox.Show("Shipper ID must be 1, 2, or 3 in Northwind.");
                    return;
                }
            }
            catch
            {
                MessageBox.Show("Ship Via must be convertible to int");
                return;
            }
    
            try
            {
                newOrder.Freight = Convert.ToDecimal(add_freightTextBox.Text);
            }
            catch
            {
                MessageBox.Show("Freight must be convertible to decimal.");
                return;
            }
    
            // Add the order into the EF model  
            context.Orders.Add(newOrder);
            ordViewSource.View.Refresh();
        }
    
        // Save the changes, either for a new customer, a new order  
        // or an edit to an existing customer or order.
        context.SaveChanges();
    }
    
    // Sets up the form so that user can enter data. Data is later  
    // saved when user clicks Commit.  
    private void AddCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        existingCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.Visibility = Visibility.Collapsed;
        newCustomerGrid.Visibility = Visibility.Visible;
    
        // Clear all the text boxes before adding a new customer.  
        foreach (var child in newCustomerGrid.Children)
        {
            var tb = child as TextBox;
            if (tb != null)
            {
                tb.Text = "";
            }
        }
    }
    
    private void NewOrder_click(object sender, RoutedEventArgs e)
    {
        var cust = custViewSource.View.CurrentItem as Customer;
        if (cust == null)
        {
            MessageBox.Show("No customer selected.");
            return;
        }
    
        existingCustomerGrid.Visibility = Visibility.Collapsed;
        newCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.UpdateLayout();
        newOrderGrid.Visibility = Visibility.Visible;
    }
    
    // Cancels any input into the new customer form  
    private void CancelCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        add_addressTextBox.Text = "";
        add_cityTextBox.Text = "";
        add_companyNameTextBox.Text = "";
        add_contactNameTextBox.Text = "";
        add_contactTitleTextBox.Text = "";
        add_countryTextBox.Text = "";
        add_customerIDTextBox.Text = "";
        add_faxTextBox.Text = "";
        add_phoneTextBox.Text = "";
        add_postalCodeTextBox.Text = "";
        add_regionTextBox.Text = "";
    
        existingCustomerGrid.Visibility = Visibility.Visible;
        newCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.Visibility = Visibility.Collapsed;
    }
    
    private void Delete_Order(Order order)
    {
        // Find the order in the EF model.  
        var ord = (from o in context.Orders.Local
                   where o.OrderID == order.OrderID
                   select o).FirstOrDefault();
    
        // Delete all the order_details that have  
        // this Order as a foreign key  
        foreach (var detail in ord.Order_Details.ToList())
        {
            context.Order_Details.Remove(detail);
        }
    
        // Now it's safe to delete the order.  
        context.Orders.Remove(ord);
        context.SaveChanges();
    
        // Update the data grid.  
        ordViewSource.View.Refresh();
    }
    
    private void DeleteOrderCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        // Get the Order in the row in which the Delete button was clicked.  
        Order obj = e.Parameter as Order;
        Delete_Order(obj);
    }
    

Spuštění aplikace WPF

  1. Ladění spustíte stisknutím klávesy F5. Ověřte, že jsou v mřížce vyplněná data zákazníků a objednávek, a navigační tlačítka fungují podle očekávání.

  2. Po zadání dat vyberte Potvrdit a přidejte do modelu nový zákazník nebo objednávku.

  3. Výběrem možnosti Storno zrušíte proces nového formuláře zákazníka nebo nové objednávky bez uložení dat.

  4. Pokud chcete provádět úpravy stávajících zákazníků a objednávek přímo, použijte textová pole, která tyto změny automaticky zapisují do modelu.