Freigeben über


Erstellen einer einfachen Datenanwendung mit WPF und Entity Framework 6

Warnung

Wenn Sie Visual Studio 2022 verwenden, sollten Sie Visual Studio 2022, Version 17.3 Preview 3 oder höher, für dieses Lernprogramm verwenden.

In dieser exemplarischen Vorgehensweise wird gezeigt, wie Sie eine einfache Anwendung "Formulare über Daten" in Visual Studio erstellen. Die App verwendet SQL Server LocalDB, die Northwind-Datenbank, Entity Framework 6 (nicht Entity Framework Core) und Windows Presentation Foundation für .NET Framework (nicht .NET Core oder .NET 5 oder höher). Dabei wird veranschaulicht, wie Sie grundlegende Datenbindungen mit einer Masterdetailansicht durchführen können, und enthält einen benutzerdefinierten Bindungsnavigator mit den Schaltflächen Weiter, Zurück, Anfang, Ende, Aktualisieren und Löschen.

Dieser Artikel konzentriert sich auf die Verwendung von Datentools in Visual Studio und versucht nicht, die zugrunde liegenden Technologien in keiner Tiefe zu erläutern. Es wird davon ausgegangen, dass Sie mit XAML, Entity Framework und SQL vertraut sind. In diesem Beispiel wird auch keine Model-View-ViewModel(MVVM)-Architektur veranschaulicht, die standard für WPF-Anwendungen ist. Sie können diesen Code jedoch mit wenigen Änderungen in Ihre eigene MVVM-Anwendung kopieren.

Der letzte Code für dieses Lernprogramm finden Sie in GitHub unter Visual Studio Tutorial Samples - EF6.

Installieren und Herstellen einer Verbindung mit Northwind

In diesem 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.

  1. Wenn Sie nicht über SQL Server Express LocalDB verfügen, installieren Sie sie über das Visual Studio Installer-. Im Visual Studio Installerkönnen Sie SQL Server Express LocalDB als Teil der Datenspeicher und Verarbeitung Workload oder als einzelne Komponente installieren.

  2. Installieren Sie die Northwind-Beispieldatenbank, indem Sie die folgenden Schritte ausführen:

    1. Öffnen Sie in Visual Studio das fenster SQL Server-Objekt-Explorer. (SQL Server Object Explorer wird als Teil des Data storage and processing Workloads im Visual Studio Installerinstalliert.) Erweitern Sie den SQL Server-Knoten . Klicken Sie mit der rechten Maustaste auf Ihre LocalDB-Instanz, und wählen Sie neue Abfrageaus.

      Ein Abfrage-Editor-Fenster wird geöffnet.

    2. Kopieren Sie das Northwind-Transact-SQL-Skript in die Zwischenablage. Dieses T-SQL-Skript erstellt die Northwind-Datenbank von Grund auf neu und füllt sie mit Daten auf.

    3. Fügen Sie das T-SQL-Skript in den Abfrage-Editor ein, und wählen Sie dann die Schaltfläche Ausführen aus.

      Nach kurzer Zeit wird die Ausführung der Abfrage abgeschlossen, und die Northwind-Datenbank wird erstellt.

  3. Neue Verbindungen für Northwind hinzufügen.

Konfigurieren des Projekts

  1. Erstellen Sie in Visual Studio ein neues C#-WPF-App (.NET Framework) Projekt.

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

  3. Klicken Sie im NuGet-Paket-Manager auf den Link Durchsuchen. Entity Framework ist wahrscheinlich das oberste Paket in der Liste. Klicken Sie im rechten Bereich auf Installieren, und folgen Sie den Anweisungen. Das Ausgabefenster teilt Ihnen mit, wann die Installation abgeschlossen ist.

    Screenshot: NuGet-Paket „Entity Framework“

    Screenshot mit Entity Framework NuGet-Paket.

  4. Jetzt können Sie Visual Studio verwenden, um ein Modell basierend auf der Northwind-Datenbank zu erstellen.

Erstellen des Modells

  1. Klicken Sie mit der rechten Maustaste auf den Projektknoten im Projektmappen-Explorer, und wählen Sie >Neues Element hinzufügenaus. Wählen Sie im linken Bereich unter dem Knoten C# Daten und im mittleren Bereich ADO.NET Entitätsdatenmodellaus.

    Screenshot des Neuen Elements des Entity Framework-Modells.

    Screenshot des Neuen Elements des Entity Framework-Modells.

  2. Geben Sie dem Modell den Namen Northwind_model, und wählen Sie Hinzufügen aus. Der Assistent für Entity Data Model wird geöffnet. Wählen Sie EF Designer aus datenbankaus, und wählen Sie dann Nextaus.

    Screenshot des EF-Modells aus datenbank.

  3. Wählen Sie im nächsten Bildschirm Die LocalDB Northwind-Verbindung (z. B. (localdb)\MSSQLLocalDB) aus, geben Sie die Northwind-Datenbank an, und klicken Sie auf Weiter.

    Wenn keine Verbindung angezeigt wird, wählen Sie Neue Verbindung und im Dialogfeld Datenquelle wählen die Optionen Microsoft SQL Server und Weiter aus. Geben Sie im Dialogfeld Verbindungseigenschaften (localdb)\MSSQLLocalDB ein, und wählen Sie unter Datenbankname auswählen oder eingeben die Option Northwind aus. Klicken Sie dann auf OK.

  4. Wenn Sie dazu aufgefordert werden, wählen Sie die Version von Entity Framework aus, die Sie verwenden.

    Screenshot mit den Versionsauswahlmöglichkeiten.

  5. 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 in der Strukturansicht den Knoten „dbo“, und wählen Sie Customers, Orders und Order Details aus. Lassen Sie die Standardeinstellungen aktiviert, und klicken Sie auf Fertig stellen.

    Screenshot der Auswahl von Datenbankobjekten für das Modell.

  6. Der Assistent generiert die C#-Klassen, die das Entity Framework-Modell darstellen. Die Klassen sind einfache C#-Standardklassen, die wir an die WPF-Benutzeroberfläche binden. Die datei .edmx beschreibt die Beziehungen und andere Metadaten, die die Klassen objekten in der Datenbank ordnet. Die .tt Dateien sind T4-Vorlagen, die den Code generieren, der auf dem Modell ausgeführt wird, und speichert Änderungen an der Datenbank. Sie können alle diese Dateien im Projektmappen-Explorer unter dem Knoten Northwind_model anzeigen:

    Screenshot zeigt Dateien des Entity Framework-Modells im Lösungs-Explorer.

    Screenshot: Dateien des Entity Framework-Modells im Projektmappen-Explorer

    Mit der Designeroberfläche für die datei .edmx können Sie einige Eigenschaften und Beziehungen im Modell ändern. Wir werden in diesem Leitfaden den Designer nicht verwenden.

  7. Die .tt-Dateien sind für allgemeine Zwecke und Sie müssen eine davon anpassen, damit sie mit der WPF-Datenbindung funktioniert, die ObservableCollections erfordert. Erweitern Sie im Projektmappen-Explorer den Knoten „Northwind_model“, bis Northwind_model.tt anzeigt wird. (Stellen Sie sicher, dass Sie sich nicht in der .Context.tt-Datei befinden, die sich direkt unter der .edmx-Datei befindet.)

  8. Drücken Sie F5- oder STRG+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

Es ist möglich, Ihren eigenen Datenbindungscode zu schreiben, aber es ist viel einfacher, Visual Studio dies für Sie zu ermöglichen.

  1. Wählen Sie im Hauptmenü die Optionen Projekt>Neue Datenquelle hinzufügen, um den Konfigurations-Assistent für Datenquellen zu öffnen. Wählen Sie Objekt aus, da Sie an die Modellklassen binden, nicht an die Datenbank:

    Screenshot: Konfigurations-Assistent für Datenquellen mit Objektquelle

  2. Erweitern Sie den Knoten für Ihr Projekt, und wählen Sie Customer aus. (Die Quellen für Aufträge werden automatisch aus der Navigations-Eigenschaft "Orders" im "Customer" generiert.)

    Screenshot mit dem Hinzufügen von Entitätsklassen als Datenquellen.

    Screenshot mit dem Hinzufügen von Entitätsklassen als Datenquellen.

  3. Klicken Sie auf Beenden.

  4. Navigieren Sie in der Codeansicht zu MainWindow.xaml. Wir behalten den XAML-Code für die Zwecke dieses Beispiels einfach. Ändern Sie den Titel von MainWindow in etwas Aussagekräftigeres und erhöhen Sie vorerst die Höhe und Breite auf 600 x 800. Sie können sie später jederzeit ändern. Fügen Sie nun 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>
    
  5. Öffnen Sie nun MainWindow.xaml, damit Sie es im Designer betrachten können. Dies bewirkt, dass das Fenster Datenquellen als Option im Fensterrand von Visual Studio neben der Toolboxangezeigt wird. Klicken Sie auf die Registerkarte, um das Fenster zu öffnen, oder drücken Sie Shift+Alt+D, oder wählen Sie Ansicht>Andere Fenster>Datenquellen. Mit den folgenden Schritten wird jede Eigenschaft der Klasse „Customers“ in einem eigenen Textfeld anzeigt. Klicken Sie zunächst auf den Pfeil im Kombinationsfeld Customers, und wählen Sie Details aus. Ziehen Sie dann den Knoten auf den mittleren Teil der Entwurfsoberfläche, damit der Designer weiß, dass er in der mittleren Zeile platziert werden soll. Wenn Sie die Zeile falsch platzieren, können Sie die Zeile später im XAML-Code (Grid.Row="1") manuell angeben. Standardmäßig werden die Steuerelemente vertikal in einem Rasterelement platziert, aber an diesem Punkt können Sie sie wie im Formular anordnen. Es kann z. B. sinnvoll sein, das textfeld Name oberhalb der Adresse zu platzieren. In der Beispielanwendung in diesem Artikel werden die Felder neu sortiert und in zwei Spalten neu organisiert.

    Screenshot: Bindung der Datenquelle „Customers“ an einzelne Steuerelemente

    Screenshot: Bindung der Datenquelle „Customers“ an einzelne Steuerelemente

    In der XAML-Ansicht sehen Sie nun ein neues Grid Element in Zeile 1 (die mittlere Zeile) des übergeordneten Rasters. Das übergeordnete Raster verfügt über ein DataContext-Attribut, das auf eine CollectionViewSource verweist, die dem Windows.Resources-Element hinzugefügt wurde. Angesichts dieses Datenkontexts wird dieser Name, wenn das erste Textfeld an Addressgebunden wird, der eigenschaft Address im aktuellen Customer-Objekt im CollectionViewSourcezugeordnet.

    <Grid DataContext="{StaticResource customerViewSource}">
    
  6. Wenn ein Kunde in der oberen Hälfte des Fensters 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 wie erwartet funktioniert, ist es wichtig, dass Sie die Bindung an die Eigenschaft „Orders“ in der Klasse „Customers“ und nicht an den separaten Knoten „Orders“ vornehmen. Ziehen Sie die Eigenschaft „Orders“ der Klasse „Customers“ in die untere Formularhälfte, damit der Designer sie in Zeile 2 platziert:

    Screenshot: Ziehen und Ablegen von Klassen für „Orders“ als Raster

    Screenshot: Ziehen und Ablegen von Klassen für „Orders“ als Raster

  7. Visual Studio hat den gesamten Bindungscode generiert, der die UI-Steuerelemente mit Ereignissen im Modell verbindet. Um einige Daten anzuzeigen, müssen Sie lediglich Code schreiben, um das Modell aufzufüllen. Navigieren Sie zunächst zu MainWindow.xaml.cs, und fügen Sie der MainWindow-Klasse für den Datenkontext ein Datenelement hinzu. Dieses Objekt, das für Sie generiert wurde, dient einem Steuerelement, das Änderungen und Ereignisse im Modell nachverfolgt. Außerdem fügen Sie CollectionViewSource-Datenelemente für Kunden und Bestellungen sowie die zugehörige Konstruktorinitialisierungslogik zum vorhandenen Konstruktor MainWindow() hinzu. Die Spitze 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 dies noch nicht geschehen ist, fügen Sie eine using Direktive für System.Data.Entity hinzu, um die Load Erweiterungsmethode in den Gültigkeitsbereich zu bringen:

    using System.Data.Entity;
    

    Scrollen Sie weiter nach unten und suchen Sie den Window_Loaded-Ereignishandler. Beachten Sie, dass Visual Studio ein CollectionViewSource-Objekt hinzugefügt hat. Dies stellt das NorthwindEntities -Objekt dar, das Sie beim Erstellen des Modells ausgewählt haben. Sie haben das bereits hinzugefügt, sodass Sie es hier nicht benötigen. Ersetzen wir den Code in Window_Loaded, damit die Methode jetzt 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;
    }
    
  8. Drücken Sie F5. Die Details für den ersten Kunden, der in collectionViewSource abgerufen wurde, sollten angezeigt werden. Sie sollten auch deren Reihenfolge im Datenraster sehen. Die Formatierung ist nicht großartig, also lassen Sie uns dies beheben. 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. Sie benötigen auch einige "Formulare" (die eigentlich Grids sind), damit der Benutzer einen neuen Kunden oder eine neue Bestellung hinzufügen kann. Um einen neuen Kunden und eine neue Bestellung hinzufügen zu können, benötigen Sie einen separaten Satz von Textfeldern, die nicht an die CollectionViewSourcegebunden 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.

Fügen Sie dem Element Windows.Resources in MainWindow.xaml zunächst diese Stile 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 als Nächstes 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 Windows Forms-Anwendungen erhalten Sie ein BindingNavigator-Objekt mit Schaltflächen zum Navigieren durch Zeilen in einer Datenbank und ausführen grundlegender CRUD-Vorgänge. WPF stellt keinen BindingNavigator bereit, aber es ist einfach genug, einen zu erstellen. Dazu verwenden Sie Schaltflächen in einem horizontalen StackPanel und ordnen die Schaltflächen Befehlen zu, die an Methoden im CodeBehind gebunden sind.

Es gibt vier Teile der Befehlslogik: (1) die Befehle, (2) die Bindungen, (3) die Schaltflächen und (4) die Befehlshandler im CodeBehind.

Hinzufügen von Befehlen, Bindungen und Schaltflächen in XAML

  1. Fügen Sie zunächst die Befehle in der Datei "MainWindow.xaml" im Windows.Resources-Element 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"/>
    
  2. Ein CommandBinding-Element ordnet ein RoutedUICommand-Ereignis einer Methode im CodeBehind zu. Fügen Sie dieses CommandBindings-Element nach dem schließenden Windows.Resources-Tag hinzu:

    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource FirstCommand}" Executed="FirstCommandHandler"/>
        <CommandBinding Command="{StaticResource LastCommand}" Executed="LastCommandHandler"/>
        <CommandBinding Command="{StaticResource NextCommand}" Executed="NextCommandHandler"/>
        <CommandBinding Command="{StaticResource PreviousCommand}" Executed="PreviousCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteCustomerCommand}" Executed="DeleteCustomerCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteOrderCommand}" Executed="DeleteOrderCommandHandler"/>
        <CommandBinding Command="{StaticResource UpdateCommand}" Executed="UpdateCommandHandler"/>
        <CommandBinding Command="{StaticResource AddCommand}" Executed="AddCommandHandler"/>
        <CommandBinding Command="{StaticResource CancelCommand}" Executed="CancelCommandHandler"/>
    </Window.CommandBindings>
    
  3. Fügen Sie nun die StackPanel mit den Schaltflächen "Navigation", "Hinzufügen", "Löschen" und "Aktualisieren" hinzu. Fügen Sie Windows.Resources zunächst diesen Stil hinzu:

    <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 dann direkt hinter dem RowDefinitions für das äußere Grid-Element in Richtung des oberen Rands 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

Das CodeBehind ist bis auf die Add- und Delete-Methoden minimal gehalten. Die Navigation erfolgt durch den Aufruf von Methoden für die View-Eigenschaft von „CollectionViewSource“. Das DeleteOrderCommandHandler-Element veranschaulicht, wie Sie ein kaskadierendes Delete für eine Bestellung durchführen. Wir müssen zuerst die Order_Details löschen, die damit verknüpft sind. Der UpdateCommandHandler fügt der Sammlung einen neuen Kunden oder eine neue Bestellung hinzu, oder aktualisiert lediglich einen vorhandenen Kunden oder eine Bestellung mit den Änderungen, die der Benutzer in den Textfeldern vorgenommen hat.

Fügen Sie diese Handlermethoden der MainWindow-Klasse in MainWindow.xaml.cshinzu. Wenn Ihre CollectionViewSource für die Tabelle "Customers" einen anderen Namen hat, müssen Sie den Namen in jeder dieser 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 Anwendung

Um mit dem Debuggen zu beginnen, drücken Sie F5. Sie sollten Kunden- und Bestelldaten im Raster aufgefüllt sehen, und die Navigationsschaltflächen sollten wie erwartet funktionieren. Klicken Sie auf Commit, um dem Modell einen neuen Kunden oder auftrag hinzuzufügen, nachdem Sie die Daten eingegeben haben. Klicken Sie auf Abbrechen, um von einem neuen Kunden- oder neuen Bestellformular zurückzugehen, ohne die Daten zu speichern. Sie können vorhandene Kunden und Bestellungen direkt in den Textfeldern bearbeiten, und diese Änderungen werden automatisch in das Modell geschrieben.