Vytvoření jednoduché datové aplikace pomocí WPF a Entity Framework 6
Upozorňující
Pokud používáte Visual Studio 2022, měli byste pro účely tohoto kurzu použít Visual Studio 2022 verze 17.3 Preview 3 nebo novější.
Tento návod ukazuje, jak v sadě Visual Studio vytvořit základní "formuláře nad daty". Aplikace používá SQL Server LocalDB, databázi Northwind, Entity Framework 6 (nikoli Entity Framework Core) a Windows Presentation Foundation pro .NET Framework (ne .NET Core nebo .NET 5 nebo novější). Ukazuje, jak provádět základní vazby dat pomocí zobrazení podrobností předlohy a má také vlastní navigátor vazeb s tlačítky pro Přesunout další, Přesunout předchozí, Přesunout na začátek, Přesunout na konec, Aktualizovat a Odstranit.
Tento článek 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 XAML, Entity Frameworku a SQL. Tento příklad také neukazuje architekturu Model-View-ViewModel (MVVM), což je standard pro aplikace WPF. Tento kód ale můžete zkopírovat do vlastní aplikace MVVM s několika úpravami.
Konečný kód pro tento kurz najdete na GitHubu na kurzech sady Visual Studio – EF6.
Instalace a připojení k Northwind
Tento příklad používá SQL Server Express LocalDB a ukázkovou databázi Northwind. Pokud ADO.NET zprostředkovatele dat pro tento produkt podporuje Entity Framework, měl by fungovat i s dalšími databázovými produkty SQL.
Pokud nemáte SQL Server Express LocalDB, nainstalujte ho prostřednictvím Instalační program pro Visual Studio. V Instalační program pro Visual Studio můžete sql Server Express LocalDB nainstalovat jako součást úlohy ukládání a zpracování dat nebo jako jednotlivé komponenty.
Následujícím postupem nainstalujte ukázkovou databázi Northwind:
V sadě Visual Studio otevřete okno Průzkumník objektů SQL Serveru. (SQL Server Průzkumník objektů se instaluje jako součást úlohy ukládání a zpracování dat v Instalační program pro Visual Studio.) 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 do schránky skript Northwind Transact-SQL. Tento skript T-SQL vytvoří zcela novou databázi Northwind a naplní ji daty.
Vložte skript T-SQL do editoru dotazů a pak zvolte tlačítko Spustit .
Po krátké době se dotaz dokončí a vytvoří se databáze Northwind.
Přidejte nová připojení pro Northwind.
Konfigurace projektu
V sadě Visual Studio vytvořte nový projekt aplikace WPF jazyka C# (.NET Framework).
Přidejte balíček NuGet pro Entity Framework 6. V Průzkumník řešení vyberte uzel projektu. V hlavní nabídce zvolte Spravovat balíčky NuGet projectu>.
V Správce balíčků NuGet klikněte na odkaz Procházet. Entity Framework je pravděpodobně hlavním balíčkem v seznamu. Klikněte na Nainstalovat v pravém podokně a postupujte podle pokynů. Okno Výstup vám řekne, až se instalace dokončí.
Teď můžete pomocí sady Visual Studio vytvořit model založený na databázi Northwind.
Vytvoření modelu
Klikněte pravým tlačítkem na uzel projektu v Průzkumník řešení a zvolte Přidat>novou položku. V levém podokně v uzlu C# zvolte Data a v prostředním podokně zvolte ADO.NET Entity Data Model.
Zavolejte model
Northwind_model
a zvolte Přidat. Otevře se Průvodce datovým modelem entity. V databázi zvolte EF Designer a potom klepněte na tlačítko Další.Na další obrazovce zvolte připojení LocalDB Northwind (například (localdb)\MSSQLLocalDB), zadejte databázi Northwind a klikněte na Další.
Pokud připojení nevidíte, zvolte Nový Připojení ion, pak v dialogovém okně Zvolit zdroj dat zvolte Microsoft SQL Server, zvolte Pokračovat a v dialogovém okně Vlastnosti Připojení ion zadejte
(localdb)\MSSQLLocalDB
a v části Vyberte nebo zadejte název databáze, zvolte Northwind a pak stiskněte OK.Pokud se zobrazí výzva, zvolte verzi entity Framework, kterou používáte.
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 ve stromovém zobrazení a zvolte Zákazníci, Objednávky a Podrobnosti objednávky. Ponechte zaškrtnuté výchozí hodnoty a klikněte na Dokončit.
Průvodce vygeneruje třídy jazyka C#, které představují model Entity Framework. Třídy jsou prosté staré třídy jazyka C# a jsou to, co jsme databindovali s uživatelským rozhraním WPF. 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. Všechny tyto soubory můžete zobrazit v Průzkumník řešení pod uzlem Northwind_model:Plocha návrháře pro
.edmx
soubor umožňuje upravit některé vlastnosti a relace v modelu. V tomto názorném postupu nebudeme používat návrháře.Soubory
.tt
jsou pro obecné účely a potřebujete jeden z nich upravit pro práci s datovou vazbou WPF, což vyžaduje ObservableCollections. V Průzkumník řešení rozbalte uzel Northwind_model, dokud nenajdete Northwind_model.tt. (Ujistěte se, že se nenacházíte v souboru .Context.tt , který je přímo pod souborem.edmx
.)Nahraďte dva výskyty ICollection řetězcem ObservableCollection<T>.
Nahraďte první výskyt HashSet<T> kolem ObservableCollection<T> řádku 51. Nenahrazovat druhý výskyt HashSet.
Nahraďte jediný výskyt System.Collections.Generic (kolem řádku 431) řetězcem System.Collections.ObjectModel.
Stisknutím klávesy F5 nebo Ctrl+F5 sestavte a spusťte projekt. Při prvním spuštění aplikace jsou třídy modelu viditelné v průvodci zdroji dat.
Nyní jste připraveni připojit tento model na stránku XAML, abyste mohli zobrazit, procházet a upravovat data.
Datová vazba modelu na stránku XAML
Je možné napsat vlastní kód pro vytváření vazby dat, ale je mnohem jednodušší nechat Visual Studio, aby to udělal za vás.
V hlavní nabídce zvolte Project>Add new data source to bring up the Data Source Configuration Wizard. Zvolte Objekt , protože vytváříte vazbu na třídy modelu, nikoli na databázi:
Rozbalte uzel projektu a vyberte Zákazník. (Zdroje pro objednávky se automaticky generují z navigační vlastnosti Objednávky v customer.)
Klikněte na Finish (Dokončit).
V zobrazení kódu přejděte na MainWindow.xaml . Pro účely tohoto příkladu zachováváme jednoduchý kód XAML. Změňte název MainWindow na něco popisnějšího a prozatím zvyšte jeho výšku a šířku na 600 x 800. Můžete ho kdykoli později změnit. Teď přidejte tyto tři definice řádků do hlavní mřížky, jeden řádek pro navigační tlačítka, jeden pro podrobnosti zákazníka a druhý pro mřížku, která zobrazuje jejich objednávky:
<Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
Teď otevřete MainWindow.xaml , abyste ho zobrazili v návrháři. To způsobí, že se okno Zdroje dat zobrazí jako možnost v okraji okna sady Visual Studio vedle panelu nástrojů. Kliknutím na kartu otevřete okno, jinak stiskněte Shift+Alt+D nebo zvolte Zobrazit>další zdroje dat Windows.> Každou vlastnost v třídě Zákazníci zobrazíme ve vlastním samostatném textovém poli. Nejprve klikněte na šipku v poli Se seznamem Zákazníci a zvolte Podrobnosti. Potom přetáhněte uzel do prostřední části návrhové plochy, aby návrhář věděl, že má jít do prostředního řádku. Pokud ho chybně zadáte, můžete řádek zadat ručně později v kódu XAML (
Grid.Row="1"
). Ve výchozím nastavení jsou ovládací prvky umístěny svisle v prvku mřížky, ale v tomto okamžiku je můžete uspořádat, ale chcete je uspořádat ve formuláři. Může například dávat smysl umístit textové pole Název nad adresu. Ukázková aplikace pro tento článek změní pořadí polí a přeuspořádá je do dvou sloupců.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 přidaný CollectionViewSource prvekWindows.Resources
. Vzhledem k tomu, že kontext dat, když první textové pole vytvoří vazbu na Adresu, tento název je namapován naAddress
vlastnost v aktuálnímCustomer
objektu v objektuCollectionViewSource
.<Grid DataContext="{StaticResource customerViewSource}">
Když je zákazník viditelný v horní polovině okna, chcete zobrazit objednávky v dolní polovině okna. Objednávky zobrazíte v jednom ovládacím prvku zobrazení mřížky. Aby vazby dat master-detail fungovaly podle očekávání, je důležité vytvořit vazbu na vlastnost Orders ve třídě Customers, nikoli na samostatný uzel Orders. Přetáhněte vlastnost Orders třídy Customers do dolní poloviny formuláře, aby ho návrhář umístí do řádku 2:
Visual Studio vygeneroval veškerý kód vazby, který spojuje ovládací prvky uživatelského rozhraní k událostem v modelu. Vše, co potřebujete udělat, aby se zobrazila nějaká data, je napsat kód pro naplnění modelu. Nejprve přejděte na MainWindow.xaml.cs a přidejte datový člen do Třídy MainWindow pro kontext dat. Tento objekt, který byl pro vás vygenerován, funguje jako ovládací prvek, který sleduje změny a události v modelu. Přidáte také datové členy CollectionViewSource pro zákazníky a objednávky a přidruženou logiku inicializace konstruktoru do existujícího konstruktoru
MainWindow()
. Horní čá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 tam ještě není, přidejte direktivu
using
pro System.Data.Entity, která metoduLoad
rozšíření přenese do oboru:using System.Data.Entity;
Teď se posuňte dolů a vyhledejte obslužnou rutinu
Window_Loaded
události. Všimněte si, že Sada Visual Studio přidala objekt CollectionViewSource. Představuje NorthwindEntities objekt, který jste vybrali při vytváření modelu. Už jste to přidali, takže ho tady nepotřebujete. Pojďme nahradit kódWindow_Loaded
tak, aby metoda teď 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 klávesu F5. Měli byste vidět podrobnosti pro prvního zákazníka, který byl načten do CollectionViewSource. Měli byste také vidět jejich objednávky v datové mřížce. Formátování není skvělé, takže to opravíme. 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, takže tady poskytneme konečný KÓD XAML, který se má zkopírovat do kódu. K tomu, aby uživatel mohl přidat nového zákazníka nebo objednávku, potřebujete také nějaké "formuláře" (které jsou ve skutečnosti gridy). Abyste mohli přidat nového zákazníka a objednávku, potřebujete samostatnou sadu textových polí, která nejsou svázaná s daty CollectionViewSource
. Určujete, která mřížka se uživateli v daném okamžiku zobrazí, nastavením 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.
Nejprve přidejte tyto styly do elementu Windows.Resources
v MainWindow.xaml:
<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>
Dále 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 model Windows Forms aplikacích získáte objekt BindingNavigator s tlačítky pro procházení řádků v databázi a provádění základních operací CRUD. WPF neposkytuje BindingNavigator, ale stačí ho vytvořit. Uděláte to pomocí tlačítek uvnitř vodorovného objektu StackPanel a přidružíte tlačítka k příkazům, které jsou svázané s metodami v kódu za sebou.
Logika příkazů má čtyři části: (1) příkazy, (2) vazby, (3) tlačítka a (4) obslužné rutiny příkazů v kódu.
Přidání příkazů, vazeb a tlačítek v XAML
Nejprve přidejte příkazy do souboru MainWindow.xaml uvnitř elementu
Windows.Resources
:<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"/>
CommandBinding mapuje
RoutedUICommand
událost na metodu v kódu za sebou. Přidejte tentoCommandBindings
prvek zaWindows.Resources
uzavírací značku:<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>
Teď přidejte
StackPanel
tlačítka navigace, přidejte, odstraňte a aktualizujte. Nejprve 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>
Za druhé 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í obslužných rutin příkazů do třídy MainWindow
Kód je minimální s výjimkou metod add a delete. Navigace se provádí voláním metod pro View Vlastnost CollectionViewSource. Ukazuje DeleteOrderCommandHandler
, jak provést kaskádové odstranění v objednávce. Musíme nejprve odstranit Order_Details, které jsou k němu přidružené. Přidá UpdateCommandHandler
nového zákazníka nebo objednávku do kolekce nebo jenom aktualizuje existujícího zákazníka nebo objednávku pomocí změn, které uživatel provedl v textových polích.
Přidejte tyto metody obslužné rutiny do třídy MainWindow v MainWindow.xaml.cs. Pokud má vaše KolekceViewSource pro tabulku Customers jiný název, musíte upravit název v každé z těchto metod:
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
Ladění spustíte stisknutím klávesy F5. V mřížce byste měli vidět data zákazníků a objednávek a navigační tlačítka by měla fungovat podle očekávání. Po zadání dat klikněte na Potvrdit a přidejte do modelu nový zákazník nebo objednávku. Kliknutím na tlačítko Storno se vrátíte z nového formuláře zákazníka nebo nové objednávky bez uložení dat. Úpravy stávajících zákazníků a objednávek můžete provádět přímo v textových polích a tyto změny se do modelu zapisují automaticky.
Související obsah
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro