Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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:
- 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.
- V instalačním programu sady Visual Studio zvolte Upravit vedle verze sady Visual Studio, kterou chcete upravit.
- Vyberte kartu Jednotlivé komponenty a pak v části Aktivity vývoje zvolte Windows Communication Foundation.
- 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:
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ů.
Zkopírujte skript Northwind Transact-SQL (T-SQL) do své schránky.
Vložte skript T-SQL do editoru dotazů a pak zvolte Spustit.
Dotaz skriptu T-SQL vytvoří databázi Northwind a naplní ji daty.
Přidejte nová připojení pro databázi Northwind.
Konfigurace projektu aplikace WPF
Pokud chcete nakonfigurovat projekt aplikace WPF, postupujte takto:
V sadě Visual Studio vytvořte nový projekt aplikace WPF (.NET Framework) C#.
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.
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í.
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:
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.
Jako název zadejte Northwind_model a pak zvolte Přidat.
V Průvodci modelem Entity Data Model zvolte EF Designer z databáze a pak vyberte Další.
V části Zvolte datové připojení vyberte připojení LocalDB Northwind (například (localdb)\MSSQLLocalDB) a pak vyberte Další.
Pokud se připojení nezobrazuje:
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.
V dialogovém okně Vlastnosti připojení zadejte (localdb)\MSSQLLocalDB jako název serveru.
V části Vyberte nebo zadejte název databáze, vyberte Northwind a pak vyberte OK.
V části Zvolit datové připojení vyberte připojení LocalDB Northwind a vyberte Další.
Pokud se zobrazí výzva, zvolte verzi entity Framework, kterou používáte, a pak vyberte Další.
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.
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
.edmxpopisuje relace a další metadata, která přidružují třídy k objektům v databázi.Soubory
.ttjsou š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 :
Návrhář formulářů pro
.edmxsoubor 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:
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:
Nahraďte dva výskyty ICollectionObservableCollection<T>.
Nahraďte první výskyt HashSet<T> za ObservableCollection<T> poblíž řádku 51. Nenahrazovat druhý výskyt HashSet.
Nahraďte jediný výskyt System.Collections.Generic (v blízkosti řádku 431) System.Collections.ObjectModel.
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:
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ší.
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.
V Průzkumníku řešení poklikejte na MainWindow.xaml v projektu a upravte XAML. Změňte
Titlez MainWindow na něco přehlednějšího a zvyšteHeightaWidthna 600 a 800 (tyto hodnoty můžete později změnit, pokud to bude nutné).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:
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ů.
Pokud chcete otevřít okno Zdroje dat, vyberte kartu Zdroje dat nebo v nabídce zvolte Zobrazit> dalšízdroje dat>.
Ve zdrojích dat vyberte Zákazníci a pak v rozevíracím seznamu vyberte Podrobnosti .
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ů.
V zobrazení XAML teď uvidíte nový
Gridprvek v řádku 1 (prostřední řádek) nadřazené mřížky. Nadřazená mřížka máDataContextatribut, který odkazuje na CollectionViewSource prvek, který patří kWindows.Resourceselementu. Vzhledem k kontextu dat, když první textové pole naváže na Address, tento název je namapován naAddressvlastnost v aktuálnímCustomerobjektu vCollectionViewSource.<Grid DataContext="{StaticResource customerViewSource}">OrderPřetáhněte vlastnost objektuCustomerstří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
Ordersvlastnost veCustomerstřídě, nikoli na samostatnýOrdersuzel.
Visual Studio teď vygeneruje veškerý kód vazby, který připojí ovládací prvky uživatelského rozhraní k událostem v modelu.
Pokud chcete zobrazit nějaká data, napište kód pro naplnění modelu. Přejděte do
MainWindow.xaml.csa přidejte datového člena do třídyMainWindowpro 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.
Přidejte
CollectionViewSourcedatové členy pro zákazníky a objednávky a přidruženou logiku inicializace konstruktoru do existujícího konstruktoruMainWindow(). 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; }Pokud neexistuje, přidejte direktivu
usingproSystem.Data.Entityaby se metodaLoadrozšíření dostala do oboru:using System.Data.Entity;Posuňte se dolů a vyhledejte obslužnou rutinu
Window_Loadedudálosti. Všimněte si, že visual Studio přidaloCollectionViewSourceobjekt. Tento objekt představujeNorthwindEntitiesobjekt, který jste vybrali při vytváření modelu. Protože jste ho už přidali, nepotřebujete ho tady. Nahraďte kódWindow_Loadedtak, 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; }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.
Otevřete
MainWindow.xamla přidejte do elementuWindows.Resourcesná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>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
MainWindow.xamlDo souboru přidejte příkazy uvnitř elementuWindows.Resourcesná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"/>CommandBindingmapujeRoutedUICommandudálost na metodu v zákulisí kódu. Přidejte tentoCommandBindingsprvek zaWindows.Resourcesuzaví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>StackPanelPřidejte tlačítka navigace, přidejte, odstraňte a aktualizujte. Přidejte tento styl doWindows.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>Vložte tento kód těsně za
RowDefinitionsvnějšíGridprvek 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:
Chcete-li přejít, volejte metody na
ViewvlastnostiCollectionViewSource.Chcete-li provést kaskádové odstranění v pořadí, použijte vzorový
DeleteOrderCommandHandlerkód, jak je znázorněno v ukázce kódu. Musíte však nejprve odstranit přidruženouOrder_Detailsobjednávku .UpdateCommandHandlerPouž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.Přidejte tyto metody obslužné rutiny do třídy
MainWindowvMainWindow.xaml.cs. Pokud má vaše tabulkaCollectionViewSourceZá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
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í.
Po zadání dat vyberte Potvrdit a přidejte do modelu nový zákazník nebo objednávku.
Výběrem možnosti Storno zrušíte proces nového formuláře zákazníka nebo nové objednávky bez uložení dat.
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.