Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hinweis
Wenn Sie Visual Studio 2022 ausführen, stellen Sie sicher, dass Sie Version 17.3 oder höher für dieses Lernprogramm verwenden.
In diesem Lernprogramm wird gezeigt, wie Sie in Visual Studio eine einfache Formulare über Daten Anwendung erstellen. Die App verwendet SQL Server LocalDB, die Northwind-Datenbank, Entity Framework 6 (nicht Entity Framework Core) und Windows Presentation Foundation (WPF) für .NET Framework (nicht .NET Core oder .NET 5 oder höher). Es zeigt, wie Sie grundlegende Datenbindung mit einer Master-Detail-Ansicht durchführen, die ein benutzerdefiniertes BindingNavigator-Steuerelement mit Schaltflächen enthält, die die folgenden Aktionen ausführen: Zum ersten Element wechseln, vorheriges, nächstes, letztes Element wechseln, löschen, hinzufügen, neue Reihenfolge festlegen, aktualisieren und abbrechen.
Dieses Lernprogramm konzentriert sich auf die Verwendung von Datentools in Visual Studio und versucht nicht, die zugrunde liegenden Technologien ausführlich zu erläutern. Es wird davon ausgegangen, dass Sie mit Extensible Application Markup Language (XAML), Entity Framework und SQL vertraut sind. Obwohl der Code in diesem Lernprogramm keine Model-View-ViewModel (MVVM)-Architektur veranschaulicht, die für WPF-Anwendungen standard ist, können Sie den Code mit einigen Änderungen in Ihre eigene MVVM-Anwendung kopieren.
Um den endgültigen Code für dieses Lernprogramm anzuzeigen, sehen Sie sich die Visual Studio-Lernprogrammbeispiele - EF6 an.
In diesem Tutorial erfahren Sie:
- Installieren und Herstellen einer Verbindung mit Northwind
- Konfigurieren des WPF-App-Projekts
- Erstellen des ADO.NET Entitätsdatenmodells
- Datenbindung des Modells an die XAML-Seite
- Anpassen des Seitenentwurfs und Hinzufügen von Rastern
- Hinzufügen von Schaltflächen zum Navigieren, Hinzufügen, Aktualisieren und Löschen
- Ausführen der WPF-Anwendung
Voraussetzungen
Visual Studio mit installierter .NET Desktop Development-Workload und der installierten Windows Communication Foundation-Komponente . So installieren Sie sie:
- Öffnen Sie die Visual Studio Installer-App , oder wählen Sie im Visual Studio-Menü "Tools>und Features abrufen " aus.
- Wählen Sie in Visual Studio Installer die Option "Ändern" neben der Version von Visual Studio aus, die Sie ändern möchten.
- Wählen Sie die Registerkarte "Einzelne Komponenten" und dann unter "Entwicklungsaktivitäten" die Option "Windows Communication Foundation" aus.
- Wählen Sie aus, umzu ändern.
SQL Server Express LocalDB. Wenn Sie nicht über SQL Server Express LocalDB verfügen, können Sie sie über die SQL Server-Downloadseiteinstallieren. Sie können sie auch mit der Visual Studio Installer-App als einzelne Komponente installieren.
SQL Server-Objekt-Explorer. Um sie zu installieren, installieren Sie die Speicher- und Verarbeitungsauslastung in der Visual Studio Installer-App .
Entity Framework 6-Tools. Dies wird in der Regel installiert, wenn Sie die .NET Dektop Development-Workload installieren.
Installieren und Herstellen einer Verbindung mit Northwind
Im folgenden Beispiel wird SQL Server Express LocalDB und die Northwind-Beispieldatenbank verwendet. Wenn der ADO.NET Datenanbieter für dieses Produkt Entity Framework unterstützt, sollte er auch mit anderen SQL-Datenbankprodukten verwendet werden.
Installieren Sie die Northwind-Beispieldatenbank, indem Sie die folgenden Schritte ausführen:
Öffnen Sie in Visual Studio das SQL Server-Objekt-Explorer-Fenster im Menü "Ansicht ". Erweitern Sie den SQL Server-Knoten . Klicken Sie mit der rechten Maustaste auf Ihre LocalDB-Instanz, und wählen Sie "Neue Abfrage" aus.
Ein Abfrage-Editor-Fenster wird geöffnet.
Kopieren Sie das T-SQL-Skript (Northwind Transact-SQL) in die Zwischenablage.
Fügen Sie das T-SQL-Skript in den Abfrage-Editor ein, und wählen Sie dann "Ausführen" aus.
Die T-SQL-Skriptabfrage erstellt die Northwind-Datenbank und füllt sie mit Daten auf.
Fügen Sie neue Verbindungen für die Northwind-Datenbank hinzu.
Konfigurieren des WPF-App-Projekts
Führen Sie die folgenden Schritte aus, um das WPF-App-Projekt zu konfigurieren:
Erstellen Sie in Visual Studio ein neues C#-WPF-App (.NET Framework) Projekt.
Fügen Sie das NuGet-Paket für Entity Framework 6 hinzu. Wählen Sie im Projektmappen-Explorer den Projektknoten aus. Wählen Sie im Hauptmenü Project>Manage NuGet Packagesaus.
Wählen Sie im NuGet-Paket-Manager den Link "Durchsuchen" aus . Suchen Sie nach dem EntityFramework-Paket , und wählen Sie es aus. Wählen Sie " Installieren" im rechten Bereich aus, und folgen Sie den Anweisungen.
Das Ausgabefenster zeigt den Fortschritt an und benachrichtigt Sie, wenn die Installation abgeschlossen ist.
Sie können jetzt Visual Studio verwenden, um ein Modell basierend auf der Northwind-Datenbank zu erstellen.
Erstellen des ADO.NET Entitätsdatenmodells
Führen Sie die folgenden Schritte aus, um das ADO.NET Entitätsdatenmodell zu erstellen:
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektknoten "WPF App" und wählen Sie Neues Element> aus. Wählen Sie im linken Bereich unter dem Knoten C# Daten und im mittleren Bereich ADO.NET Entitätsdatenmodellaus.
Geben Sie Northwind_model für den Namen ein, und wählen Sie dann "Hinzufügen" aus.
Wählen Sie im Entitätsdatenmodell-AssistentenEF Designer aus der Datenbank und dann "Weiter" aus.
Wählen Sie in Ihre Datenverbindung Ihre LocalDB Northwind-Verbindung (z. B. (localdb)\MSSQLLocalDB) und wählen Sie dann "Weiter" aus.
Wenn keine Verbindung angezeigt wird:
Wählen Sie "Neue Verbindung" aus. Wenn Microsoft SQL Server nicht als Datenquelle im Dialogfeld "Verbindungseigenschaften " ausgewählt ist, wählen Sie "Ändern" aus. Wählen Sie im Dialogfeld "Datenquelle auswählen" Microsoft SQL Server und dann "OK" aus.
Geben Sie im Dialogfeld "Verbindungseigenschaften " (localdb)\MSSQLLocalDB als Servernamen ein.
Für Wählen Sie oder geben Sie einen Datenbanknamen ein wählen Sie Nordwind aus, und klicken Sie dann auf OK.
In "Ihre Datenverbindung wählen" wählen Sie Ihre LocalDB Northwind-Verbindung aus und klicken Sie auf Weiter.
Wenn Sie dazu aufgefordert werden, wählen Sie die Version von Entity Framework aus, die Sie verwenden, und wählen Sie dann "Weiter" aus.
Wählen Sie auf der nächsten Seite des Assistenten aus, welche Tabellen, gespeicherten Prozeduren und andere Datenbankobjekte in das Entity Framework-Modell einbezogen werden sollen. Erweitern Sie den dbo-Knoten unter dem Tabellen-Knoten in der Strukturansicht. Wählen Sie "Kunden", "Bestelldetails" und "Bestellungen" aus. Lassen Sie die Standardwerte ausgewählt, und wählen Sie Fertig stellen.
Der Assistent generiert die C#-Klassen, die das Entity Framework-Modell darstellen und welche Visual Studio-Daten an die WPF-Benutzeroberfläche binden. Es erstellt die folgenden Dateien in Ihrem Projekt:
Die datei
.edmxbeschreibt die Beziehungen und andere Metadaten, die die Klassen objekten in der Datenbank ordnet.Die
.ttDateien sind T4-Vorlagen, die den Code generieren, der auf dem Modell ausgeführt wird, und speichert Änderungen an der Datenbank.
Diese Dateien sind im Projektmappen-Explorer unter dem Knoten Northwind_model sichtbar:
Der Formular-Designer für die
.edmxDatei wird in diesem Lernprogramm nicht verwendet, Sie können ihn jedoch verwenden, um bestimmte Eigenschaften und Beziehungen im Modell zu ändern.
Die .tt Dateien sind allgemeinen Zwecks, und Sie müssen eine davon bearbeiten, um mit der WPF-Datenbindung zu arbeiten, die ObservableCollection Objekte erfordert. Folgen Sie diesen Schritten:
Erweitern Sie im Lösungs-Explorer den Knoten Northwind_model, bis Sie Northwind_model.tt finden. Doppelklicken Sie auf diese Datei, und nehmen Sie die folgenden Änderungen vor:
Ersetzen Sie die beiden Vorkommen von ICollection durch ObservableCollection<T>.
Ersetzen Sie das erste Vorkommen von HashSet<T> durch ObservableCollection<T> in der Nähe von Zeile 51. Ersetzen Sie nicht das zweite Vorkommen von HashSet.
Ersetzen Sie das einzige Vorkommen von System.Collections.Generic in der Nähe der Zeile 431 durch System.Collections.ObjectModel.
Drücken Sie F5, um das Projekt zu erstellen und auszuführen. Wenn die Anwendung zum ersten Mal ausgeführt wird, sind die Modellklassen für den Datenquellen-Assistenten sichtbar.
Jetzt können Sie dieses Modell mit der XAML-Seite verbinden, damit Sie die Daten anzeigen, navigieren und ändern können.
Datenbindung des Modells an die XAML-Seite
Obwohl es möglich ist, Ihren eigenen Datenbindungscode zu schreiben, ist es einfacher, Visual Studio dies für Sie zu ermöglichen. Führen Sie dazu die folgenden Schritte aus:
Wählen Sie im Hauptmenü Projekt>Neue Datenquelle hinzufügen aus, um den Datenquellenkonfigurations-Assistenten anzuzeigen. Da Sie an die Modellklassen binden, nicht an die Datenbank, wählen Sie "Objekt" aus. Wählen Sie Weiteraus.
Erweitern Sie den Knoten für Ihr Projekt, wählen Sie das Customer-Objekt und dann "Fertig stellen" aus. Die Quellen für Order-Objekte werden automatisch aus der Navigationseigenschaft Orders in Customer generiert.
Doppelklicken Sie im Projektmappen-Explorer in Ihrem Projekt auf "MainWindow.xaml ", um den XAML-Code zu bearbeiten. Ändern Sie den
Title-Wert von "MainWindow" auf einen aussagekräftigeren Namen und erhöhen Sie denHeight-Wert auf 600 und denWidth-Wert auf 800 (Sie können diese Werte bei Bedarf später ändern).Fügen Sie diese drei Zeilendefinitionen zum Hauptraster hinzu, eine Zeile für die Navigationsschaltflächen, eine für die Details des Kunden und eine für das Raster, in dem ihre Bestellungen angezeigt werden:
<Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
Als Nächstes zeigen Sie jede Eigenschaft in der Customers Klasse in einem eigenen textfeld an. Folgen Sie diesen Schritten:
Doppelklicken Sie im Projektmappen-Explorer auf MainWindow.xaml, um sie im Designer zu öffnen.
Die Registerkarte "Datenquellen " wird im linken Bereich von Visual Studio in der Nähe der Toolbox angezeigt.
Um das Fenster "Datenquellen" zu öffnen, wählen Sie die Registerkarte "Datenquellen" aus, oder wählen Sie im Menü "Andere> anzeigen>" aus.
Wählen Sie in Datenquellen " Kunden" und dann " Details " aus der Dropdownliste aus.
Ziehen Sie den Knoten in die mittlere Reihe des Entwurfsbereichs. Wenn Sie es falsch platzieren, können Sie die Zeile später manuell im XAML festlegen, indem Sie
Grid.Row="1"auswählen.Standardmäßig werden die Steuerelemente vertikal in einem Rasterelement platziert, aber Sie können sie wie im Formular anordnen. Sie können beispielsweise das Textfeld "Name " über der Adresse platzieren. In der Beispielanwendung für dieses Lernprogramm werden die Felder neu angeordnet und in zwei Spalten aufgeteilt.
In der XAML-Ansicht können Sie nun ein neues
GridElement in Zeile 1 (der mittleren Zeile) des übergeordneten Rasters sehen. Das übergeordnete Raster verfügt über einDataContextAttribut, das auf ein CollectionViewSource Element verweist, das zumWindows.ResourcesElement gehört. Angesichts dieses Datenkontexts wird beim ersten Textfeld, das an Adresse gebunden ist, dieser Name derAddress-Eigenschaft im aktuellenCustomer-Objekt inCollectionViewSourcezugeordnet.<Grid DataContext="{StaticResource customerViewSource}">Ziehen Sie die
OrderEigenschaft des Objekts derCustomersKlasse in die untere Hälfte des Formulars, sodass der Designer sie in Zeile 2 platziert.Wenn ein Kunde in der oberen Hälfte des Formulars sichtbar ist, möchten Sie deren Bestellungen in der unteren Hälfte anzeigen. Sie zeigen die Bestellungen in einem einzelnen Rasteransichtssteuerelement an. Damit die Master-Detail-Datenbindung erwartungsgemäß funktioniert, ist es wichtig, dass Sie eine Bindung an die
OrdersEigenschaft in derCustomersKlasse und nicht an den separatenOrdersKnoten festlegen.
Visual Studio generiert jetzt den gesamten Bindungscode, der die UI-Steuerelemente mit Ereignissen im Modell verbindet.
Wenn Sie einige Daten anzeigen möchten, schreiben Sie Code, um das Modell aufzufüllen. Navigieren Sie zu
MainWindow.xaml.csund fügen Sie derMainWindowKlasse ein Datenelement für den Datenkontext hinzu.Dieses Objekt, das für Sie generiert wurde, fungiert wie ein Steuerelement, das Änderungen und Ereignisse im Modell verfolgt.
Fügen Sie
CollectionViewSource-Daten-Members für Kunden und Bestellungen sowie die zugehörige Konstruktor-Initialisierungslogik zum bestehenden KonstruktorMainWindow()hinzu. Der erste Teil der Klasse sollte wie folgt aussehen: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; }Wenn die Erweiterungsmethode nicht vorhanden ist, fügen Sie eine
using-Direktive fürSystem.Data.Entity, um dieLoad-Erweiterungsmethode in den Gültigkeitsbereich zu bringen.using System.Data.Entity;Scrollen Sie nach unten, und suchen Sie den
Window_LoadedEreignishandler. Beachten Sie, dass Visual Studio einCollectionViewSourceObjekt hinzugefügt hat. Dieses Objekt stellt dasNorthwindEntitiesObjekt dar, das Sie beim Erstellen des Modells ausgewählt haben. Da Sie es bereits hinzugefügt haben, benötigen Sie es hier nicht. Ersetzen Sie den Code inWindow_Loaded, sodass die Methode wie folgt aussieht: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; }Drücken Sie F5.
Sie sollten die Details für den ersten Kunden, der im
CollectionViewSourceabgerufen wurde, und seine Bestellungen im Datenraster sehen. Sie korrigieren die Formatierung im nächsten Abschnitt. Sie können auch eine Möglichkeit zum Anzeigen der anderen Datensätze erstellen und grundlegende Erstellungs-, Lese-, Aktualisierungs- und Löschvorgänge (CRUD) durchführen.
Anpassen des Seitendesigns und Hinzufügen von Rastern für neue Kunden und Bestellungen
Die von Visual Studio erstellte Standardanordnung eignet sich nicht ideal für Ihre Anwendung, daher stellen wir hier den endgültigen XAML-Code bereit, um sie in Ihren Code zu kopieren. Außerdem benötigen Sie einige Raster, um dem Benutzer das Hinzufügen eines neuen Kunden oder einer neuen Bestellung zu ermöglichen.
Wenn Sie einen neuen Kunden und eine neue Bestellung hinzufügen möchten, erstellen Sie einen separaten Satz von Textfeldern, die nicht an die CollectionViewSourceDaten gebunden sind. Sie steuern, welches Raster der Benutzer zu einem bestimmten Zeitpunkt sieht, indem Sie die Visible-Eigenschaft in den Handlermethoden festlegen. Schließlich fügen Sie jeder Zeile im Raster "Bestellungen" eine Schaltfläche "Löschen" hinzu, damit der Benutzer eine einzelne Bestellung löschen kann.
Öffnen Sie
MainWindow.xamlund fügen Sie demWindows.ResourcesElement die folgenden Formatvorlagen hinzu.<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>Ersetzen Sie das gesamte äußere Raster durch dieses Markup:
<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>
Hinzufügen von Schaltflächen zum Navigieren, Hinzufügen, Aktualisieren und Löschen
In einer Windows Forms-Anwendung erhalten Sie ein BindingNavigator Objekt mit Schaltflächen zum Navigieren durch Zeilen in einer Datenbank und ausführen grundlegender CRUD-Vorgänge. Obwohl WPF kein Element bereitstellt BindingNavigator, ist es einfach, eine zu erstellen, indem Schaltflächen innerhalb einer horizontalen StackPanelErstellt werden, und die Schaltflächen den Befehlen zugeordnet werden, die an Methoden in der CodeBehind-Datei gebunden sind.
Es gibt vier Teile der Befehlslogik:
- Befehle
- Bindungen
- Knöpfe
- Befehlshandler im Code-behind
Hinzufügen von Befehlen, Bindungen und Schaltflächen in XAML
Fügen Sie in der
MainWindow.xamlDatei die Befehle imWindows.ResourcesElement wie folgt hinzu:<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"/>Ein
CommandBindingordnet einRoutedUICommand-Ereignis einer Methode im CodeBehind zu. Fügen Sie diesesCommandBindingsElement nach dem abschließendenWindows.Resources-Tag wie folgt ein:<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>Fügen Sie die
StackPanelSchaltflächen "Navigation", "Hinzufügen", "Löschen" und "Aktualisieren" hinzu. Fügen Sie diesen Stil zuWindows.Resourceshinzu.<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>Fügen Sie diesen Code direkt nach dem
RowDefinitionsfür das äußereGridElement nahe dem oberen Rand der XAML-Seite ein.<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>
Hinzufügen von Befehlshandlern zur MainWindow-Klasse
Der Code-Behind in MainWindow.xaml.cs ist bis auf die Methoden add und delete minimal:
Um zu navigieren, rufen Sie Methoden für die
View-Eigenschaft vonCollectionViewSourceauf.Um eine Bestellung kaskadenartig zu löschen, verwenden Sie das
DeleteOrderCommandHandlerwie im Code-Beispiel gezeigt. Allerdings müssen Sie zuerst das zugehörigeOrder_Detailsder Bestellung löschen.Verwenden Sie die
UpdateCommandHandlerMöglichkeit, der Sammlung einen Kunden oder eine Bestellung hinzuzufügen oder einen vorhandenen Kunden oder eine vorhandene Bestellung mit den Änderungen zu aktualisieren, die der Benutzer in den Textfeldern vorgibt.Fügen Sie diese Handlermethoden zur Klasse
MainWindowinMainWindow.xaml.cshinzu. Wenn IhrCollectionViewSourcefür die Tabelle Customers einen anderen Namen hat, müssen Sie den Namen in jeder der Methoden anpassen: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); }
Ausführen der WPF-Anwendung
Um mit dem Debuggen zu beginnen, drücken Sie F5. Stellen Sie sicher, dass Kunden- und Bestelldaten im Raster aufgefüllt werden, und die Navigationsschaltflächen funktionieren wie erwartet.
Wählen Sie "Commit" aus, um einen neuen Kunden oder eine Bestellung zum Modell hinzuzufügen, nachdem Sie die Daten eingegeben haben.
Wählen Sie "Abbrechen" aus, um ein neues Kunden- oder neues Bestellformular zu löschen, ohne die Daten zu speichern.
Wenn Sie vorhandene Kunden und Bestellungen direkt bearbeiten möchten, verwenden Sie die Textfelder, die diese Änderungen automatisch in das Modell schreiben.