Delen via


Een eenvoudige gegevenstoepassing maken met WPF en Entity Framework 6

Opmerking

Als u Visual Studio 2022 gebruikt, moet u versie 17.3 of hoger gebruiken voor deze zelfstudie.

In deze zelfstudie ziet u hoe u een basisformulier maakt voor een gegevenstoepassing in Visual Studio. De app maakt gebruik van SQL Server LocalDB, de Northwind-database, Entity Framework 6 (niet Entity Framework Core) en Windows Presentation Foundation (WPF) voor .NET Framework (niet .NET Core of .NET 5 of hoger). Het laat zien hoe u eenvoudige gegevensbinding kunt uitvoeren met een hoofddetailweergave en een aangepast BindingNavigator-besturingselement met knoppen die het volgende doen: eerst verplaatsen, vorige verplaatsen, volgende verplaatsen, naar laatste verplaatsen, verwijderen, toevoegen, nieuwe volgorde instellen, bijwerken en annuleren.

Deze zelfstudie is gericht op het gebruik van gegevenshulpprogramma's in Visual Studio en probeert de onderliggende technologieën niet uitgebreid uit te leggen. Hierbij wordt ervan uitgegaan dat u bekend bent met Extensible Application Markup Language (XAML), Entity Framework en SQL. Hoewel de code in deze zelfstudie geen Model-View-ViewModel-architectuur (MVVM) demonstreert, wat standaard is voor WPF-toepassingen, kunt u de code kopiëren met enkele wijzigingen in uw eigen MVVM-toepassing.

Als u de laatste code voor deze zelfstudie wilt bekijken, raadpleegt u visual Studio-zelfstudievoorbeelden - EF6.

In deze handleiding leert u:

  • Installeren en verbinding maken met Northwind
  • Het WPF-app-project configureren
  • Het ADO.NET-entiteitsgegevensmodel maken
  • Gegevens binden het model aan de XAML-pagina
  • Het paginaontwerp aanpassen en rasters toevoegen
  • Knoppen toevoegen om te navigeren, toevoegen, bijwerken en verwijderen
  • De WPF-toepassing uitvoeren

Vereiste voorwaarden

  • Visual Studio waarop de workload .NET Desktop Development is geïnstalleerd en het Windows Communication Foundation-onderdeel is geïnstalleerd. Ga als volgende te werk om het te installeren:

    1. Open de Visual Studio Installer-app of selecteer Hulpprogramma's>en functies ophalen in het menu van Visual Studio.
    2. Kies In Het installatieprogramma van Visual Studiode optie Wijzigen naast de versie van Visual Studio die u wilt wijzigen.
    3. Selecteer het tabblad Afzonderlijke onderdelen en kies vervolgens Windows Communication Foundation onder Ontwikkelingsactiviteiten.
    4. Selecteer Wijzigen.
  • SQL Server Express LocalDB. Als u geen SQL Server Express LocalDB hebt, kunt u deze installeren vanaf de downloadpagina van SQL Server. U kunt deze ook installeren met de Visual Studio Installer-app als afzonderlijk onderdeel.

  • SQL Server Object Explorer. Als u deze wilt installeren, installeert u de werkbelasting voor gegevensopslag en -verwerking in de Visual Studio Installer-app .

  • Entity Framework 6-hulpprogramma's. Dit wordt meestal geïnstalleerd wanneer u de workload .NET Dektop Development installeert.

Installeren en verbinding maken met Northwind

In het volgende voorbeeld worden SQL Server Express LocalDB en de Northwind-voorbeelddatabase gebruikt. Als de ADO.NET gegevensprovider voor dat product Entity Framework ondersteunt, moet deze ook met andere SQL-databaseproducten werken.

Installeer de Northwind-voorbeelddatabase door de volgende stappen uit te voeren:

  1. Open in Visual Studio het venster SQL Server Object Explorer in het menu Beeld . Vouw het SQL Server-knooppunt uit. Klik met de rechtermuisknop op uw LocalDB-exemplaar en selecteer Nieuwe query.

    Er wordt een venster van de queryeditor geopend.

  2. Kopieer het script Northwind Transact-SQL (T-SQL) naar het klembord.

  3. Plak het T-SQL-script in de queryeditor en kies Uitvoeren.

    De T-SQL-scriptquery maakt de Northwind-database en vult deze met gegevens.

  4. Voeg nieuwe verbindingen toe voor de Northwind-database.

Het WPF-app-project configureren

Voer de volgende stappen uit om het WPF-app-project te configureren:

  1. Maak in Visual Studio een nieuw C# WPF App (.NET Framework) project.

  2. Voeg het NuGet-pakket voor Entity Framework 6 toe. Selecteer in Solution Explorerhet projectknooppunt. Kies in het hoofdmenu Project>NuGet-pakketten beheren.

  3. Selecteer in NuGet Package Manager de koppeling Bladeren . Zoek en selecteer het EntityFramework-pakket . Selecteer Installeren in het rechterdeelvenster en volg de aanwijzingen.

    In het uitvoervenster wordt de voortgang weergegeven en wordt u op de hoogte brengt wanneer de installatie is voltooid.

    Schermopname van het Entity Framework NuGet-pakket.

U kunt nu Visual Studio gebruiken om een model te maken op basis van de Northwind-database.

Het ADO.NET-entiteitsgegevensmodel maken

Voer de volgende stappen uit om het ADO.NET entiteitsgegevensmodel te maken:

  1. Klik met de rechtermuisknop op het projectknooppunt WPF App in Solution Explorer en kiesNieuw item>. Kies in het linkerdeelvenster onder het C#-knooppunt Gegevens en kies in het middelste deelvenster ADO.NET Entiteitsgegevensmodel.

    Schermopname van het venster Nieuw item toevoegen met ADO.NET Entity Data Model geselecteerd.

  2. Voer Northwind_model in voor de naam en kies vervolgens Toevoegen.

  3. In de wizard Entiteitsgegevensmodel kies EF Designer vanuit database en selecteer Volgende.

    Schermopname die de EF Designer toont van de database die is geselecteerd in de Gegevensmodel-wizard.

  4. Selecteer in Choose Your Data Connection uw LocalDB Northwind-verbinding (bijvoorbeeld (localdb)\MSSQLLocalDB) en selecteer vervolgens Volgende.

  5. Als u geen verbinding ziet:

    1. Kies Nieuwe verbinding. Als Microsoft SQL Server niet is geselecteerd als de gegevensbron in het dialoogvenster Verbindingseigenschappen , selecteert u Wijzigen. Kies Microsoft SQL Server in het dialoogvenster Gegevensbron kiezen en selecteer VERVOLGENS OK.

    2. Voer in het dialoogvenster Verbindingseigenschappen(localdb)\MSSQLLocalDB in als servernaam.

    3. Als u een databasenaam wilt selecteren of invoeren, selecteert u Northwind en selecteert u VERVOLGENS OK.

    4. Selecteer in Choose Your Data Connection uw LocalDB Northwind-verbinding en selecteer Volgende.

  6. Als u hierom wordt gevraagd, kiest u de versie van Entity Framework die u gebruikt en selecteert u Vervolgens.

    Schermopname van de versieopties voor Entity Framework.

  7. Kies op de volgende pagina van de wizard welke tabellen, opgeslagen procedures en andere databaseobjecten moeten worden opgenomen in het Entity Framework-model. Vouw het dbo-knooppunt onder het knooppunt Tabellen uit in de structuurweergave. Selecteer Klanten, Ordergegevens en Orders. Laat de standaardinstellingen ingeschakeld en selecteer Voltooien.

    Schermopname van de geselecteerde databaseobjecten van het gegevensmodel.

    De wizard genereert de C#-klassen die het Entity Framework-model vertegenwoordigen en zijn wat Visual Studio-gegevens binden aan de WPF-gebruikersinterface. Hiermee worden de volgende bestanden in uw project gemaakt:

    • Het bestand .edmx beschrijft de relaties en andere metagegevens die de klassen aan objecten in de database koppelen.

    • De .tt bestanden zijn T4-sjablonen die de code genereren die op het model werkt en wijzigingen in de database opslaan.

    Deze bestanden zijn zichtbaar in Solution Explorer onder het knooppunt Northwind_model :

    Schermopname van de modelbestanden van Entity Framework in Solution Explorer.

    De formulierontwerper voor het .edmx bestand wordt niet gebruikt in deze zelfstudie, maar u kunt het gebruiken om bepaalde eigenschappen en relaties in het model te wijzigen.

De .tt bestanden zijn algemeen en u moet een van deze bestanden bewerken om te werken met WPF-gegevensbinding, waarvoor objecten zijn vereist ObservableCollection . Volg deze stappen:

  1. Vouw in Solution Explorer het knooppunt Northwind_model uit totdat u Northwind_model.tt hebt gevonden. Dubbelklik op dit bestand en breng de volgende wijzigingen aan:

  2. Druk op F5- om het project te bouwen en uit te voeren. Wanneer de toepassing voor het eerst wordt uitgevoerd, zijn de modelklassen zichtbaar voor de wizard gegevensbronnen.

U kunt dit model nu koppelen aan de XAML-pagina, zodat u de gegevens kunt bekijken, navigeren en wijzigen.

Gegevens binden het model aan de XAML-pagina

Hoewel het mogelijk is om uw eigen code voor gegevensbinding te schrijven, is het eenvoudiger om Visual Studio dit voor u te laten doen. Voer hiervoor de volgende stappen uit:

  1. Kies in het hoofdmenu Project>Nieuwe gegevensbron toevoegen om de wizard Gegevensbronconfiguratie weer te geven. Omdat u verbinding maakt met de modelklassen, niet aan de database, kiest u Object. Kies Volgende.

    Schermopname van de wizard Configuratie van gegevensbron met Object geselecteerd als de gegevensbron.

  2. Vouw het knooppunt voor uw project uit, selecteer het object Klant en selecteer Voltooien. De bronnen voor orderobjecten worden automatisch gegenereerd op basis van de eigenschap Orders-navigatie in Klant.

    Schermopname van het klantobject dat is geselecteerd als gegevensbron.

  3. Dubbelklik in Solution Explorer op MainWindow.xaml in uw project om de XAML te bewerken. Verander de Title van MainWindow in iets beschrijvenders en verhoog de Height en Width waarde naar 600 en 800 (u kunt deze waarden later wijzigen, indien nodig).

  4. Voeg deze drie rijdefinities toe aan het hoofdraster, één rij voor de navigatieknoppen, één voor de details van de klant en één voor het raster waarin de orders worden weergegeven:

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

Vervolgens geeft u elke eigenschap in de Customers klasse weer in een eigen afzonderlijk tekstvak. Volg deze stappen:

  1. Dubbelklik in Solution Explorer op MainWindow.xaml om deze te openen in de ontwerpfunctie.

    Het tabblad Gegevensbronnen wordt weergegeven in het linkerdeelvenster van Visual Studio in de buurt van de Werkset.

  2. Als u het venster Gegevensbronnen wilt openen, selecteert u het tabblad Gegevensbronnenof kiest> uAndere> weergeven in het menu.

  3. Selecteer klanten in gegevensbronnen en selecteer vervolgens Details in de vervolgkeuzelijst.

  4. Sleep het knooppunt naar de middelste rij van het ontwerpgebied. Als u deze verkeerd opgeeft, kunt u de rij later handmatig opgeven in de XAML door deze te Grid.Row="1"selecteren.

    Standaard worden de besturingselementen verticaal in een rasterelement geplaatst, maar u kunt ze op de gewenste manier in het formulier rangschikken. U kunt bijvoorbeeld het tekstvak Naam boven het adres plaatsen. De voorbeeldtoepassing voor deze zelfstudie herschikt de volgorde van de velden en rangschikt deze in twee kolommen.

    Schermopname die klantengegevensbron koppelt aan afzonderlijke bedieningselementen.

    In de XAML-weergave ziet u nu een nieuw Grid element in rij 1 (de middelste rij) van het bovenliggende raster. Het bovenliggende raster heeft een DataContext kenmerk dat verwijst naar een CollectionViewSource kenmerk dat deel uitmaakt van het Windows.Resources element. Gezien die gegevenscontext, wanneer het eerste tekstvak wordt gekoppeld aan Adres, wordt die naam toegewezen aan de Address eigenschap in het huidige Customer object in CollectionViewSource.

    <Grid DataContext="{StaticResource customerViewSource}">
    
  5. Sleep de eigenschap van het Orderobject van de Customers klasse naar de onderste helft van het formulier, zodat de ontwerper deze in rij 2 plaatst.

    Wanneer een klant zichtbaar is in de bovenste helft van het formulier, wilt u de orders in de onderste helft zien. U geeft de orders weer in één rasterweergavebesturingselement. Opdat de master-detailgegevensbinding zoals verwacht werkt, is het belangrijk dat u bindt aan de eigenschap in de Orders klasse, en niet aan het afzonderlijke Customers knooppunt.

    Schermopname waarop de klassen Orders als een raster zijn gesleept en neergezet.

    Visual Studio genereert nu alle bindingscode waarmee de ui-besturingselementen worden verbonden met gebeurtenissen in het model.

  6. Als u enkele gegevens wilt zien, schrijft u code om het model te vullen. Navigeer naar MainWindow.xaml.cs en voeg een gegevenslid toe aan de MainWindow-klasse voor de gegevenscontext.

    Dit object, dat voor u is gegenereerd, fungeert als een besturingselement waarmee wijzigingen en gebeurtenissen in het model worden bijgehouden.

  7. Voeg CollectionViewSource gegevensleden toe voor klanten en orders en de bijbehorende initialisatielogica van de constructor aan de bestaande constructor MainWindow(). Het eerste deel van de klas moet er als volgt uitzien:

    public partial class MainWindow : Window
    {
        NorthwindEntities context = new NorthwindEntities();
        CollectionViewSource custViewSource;
        CollectionViewSource ordViewSource;
    
        public MainWindow()
        {
            InitializeComponent();
            custViewSource = ((CollectionViewSource)(FindResource("customerViewSource")));
            ordViewSource = ((CollectionViewSource)(FindResource("customerOrdersViewSource")));
            DataContext = this;
        }
    
  8. Als het niet bestaat, voegt u een using richtlijn toe voor System.Data.Entity om de Load extensiemethode binnen het bereik te brengen.

    using System.Data.Entity;
    
  9. Scrol omlaag en zoek de Window_Loaded eventhandler. U ziet dat Visual Studio een CollectionViewSource object heeft toegevoegd. Dit object vertegenwoordigt het NorthwindEntities object dat u hebt geselecteerd toen u het model maakte. Omdat u het al hebt toegevoegd, hebt u deze hier niet nodig. Vervang de code binnen Window_Loaded zodat de methode er als volgt uitziet:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Load is an extension method on IQueryable,    
        // defined in the System.Data.Entity namespace.   
        // This method enumerates the results of the query,    
        // similar to ToList but without creating a list.   
        // When used with Linq to Entities, this method    
        // creates entity objects and adds them to the context.   
        context.Customers.Load();
    
        // After the data is loaded, call the DbSet<T>.Local property    
        // to use the DbSet<T> as a binding source.   
        custViewSource.Source = context.Customers.Local;
    }
    
  10. Druk op F5-.

    U zou de details moeten zien van de eerste klant die is opgehaald in de CollectionViewSource en hun bijbehorende orders in de datagrid. Je corrigeert de opmaak in de volgende sectie. U kunt ook een manier maken om de andere records weer te geven en basisbewerkingen voor maken, lezen, bijwerken en verwijderen (CRUD) uit te voeren.

Het paginaontwerp aanpassen en rasters toevoegen voor nieuwe klanten en orders

De standaardindeling die door Visual Studio wordt geproduceerd, is niet ideaal voor uw toepassing, dus bieden we de uiteindelijke XAML hier om naar uw code te kopiëren. U hebt ook enkele rasters nodig om de gebruiker in staat te stellen een nieuwe klant of bestelling toe te voegen.

Als u een nieuwe klant en bestelling wilt toevoegen, maakt u een afzonderlijke set tekstvakken die niet aan de CollectionViewSourcegegevens zijn gebonden. U bepaalt welk raster de gebruiker op een bepaald moment ziet door de eigenschap Zichtbaar in te stellen in de handlermethoden. Ten slotte voegt u een knop Verwijderen toe aan elke rij in het raster Orders, zodat de gebruiker een afzonderlijke order kan verwijderen.

  1. Open MainWindow.xaml en voeg de volgende stijlen toe aan het Windows.Resources element:

    <Style x:Key="Label" TargetType="{x:Type Label}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Left"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="23"/>
    </Style>
    <Style x:Key="CustTextBox" TargetType="{x:Type TextBox}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Right"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="26"/>
       <Setter Property="Width" Value="120"/>
    </Style>
    
  2. Vervang het hele buitenste raster door deze markering:

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

Knoppen toevoegen om te navigeren, toevoegen, bijwerken en verwijderen

In een Windows Forms-toepassing krijgt u een BindingNavigator object met knoppen voor het navigeren door rijen in een database en het uitvoeren van eenvoudige CRUD-bewerkingen. Hoewel WPF geen BindingNavigator biedt, is het eenvoudig er een te maken door knoppen in een horizontale StackPanel te plaatsen en deze knoppen te koppelen aan opdrachten die zijn gekoppeld aan methoden in het code-behind-bestand.

Er zijn vier onderdelen voor de opdrachtlogica:

  • Opdrachten
  • Verbindingen
  • Knopen
  • Opdrachthandlers in de code-behind

Opdrachten, bindingen en knoppen toevoegen in XAML

  1. Voeg in het MainWindow.xaml bestand de opdrachten in het Windows.Resources element als volgt toe:

    <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. Een CommandBinding gebeurtenis wordt RoutedUICommand toegewezen aan een methode in de code achter. Voeg dit CommandBindings element toe na de Windows.Resources afsluitende tag als volgt:

    <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. Voeg de StackPanel knoppen toe met de navigatie, voeg de knoppen toe, verwijder en werk deze bij. Voeg deze stijl toe aan Windows.Resources:

    <Style x:Key="NavButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
        <Setter Property="FontSize" Value="24"/>
        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
        <Setter Property="Margin" Value="2,2,2,0"/>
        <Setter Property="Width" Value="40"/>
        <Setter Property="Height" Value="auto"/>
    </Style>
    
  4. Plak deze code direct na het RowDefinitions voor het buitenste Grid-element, richting de bovenkant van de XAML-pagina.

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

Opdrachthandlers toevoegen aan de klasse MainWindow

De code-behind is MainWindow.xaml.cs minimaal, met uitzondering van de methoden voor toevoegen en verwijderen:

  1. Om te navigeren, roept u methoden aan op de eigenschap View van CollectionViewSource.

  2. Als u een trapsgewijs verwijderen wilt uitvoeren voor een order, gebruikt u de DeleteOrderCommandHandler code die wordt weergegeven in het codevoorbeeld. U moet echter eerst de gekoppelde Order_Detailsbestelling verwijderen.

  3. Gebruik de UpdateCommandHandler opdracht om een klant of order toe te voegen aan de verzameling, of een bestaande klant of bestelling bij te werken met de wijzigingen die de gebruiker in de tekstvakken aanbrengt.

  4. Voeg deze handlermethoden toe aan de MainWindow klasse in MainWindow.xaml.cs. Als uw CollectionViewSource-tabel Klanten een andere naam heeft, moet u de naam in elke van de methoden aanpassen:

    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);
    }
    

De WPF-toepassing uitvoeren

  1. Druk op F5om foutopsporing te starten. Controleer of de klant- en ordergegevens in het raster zijn ingevuld en of de navigatieknoppen werken zoals verwacht.

  2. Selecteer Doorvoeren om een nieuwe klant of order toe te voegen aan het model nadat u de gegevens hebt ingevoerd.

  3. Selecteer Annuleren om een nieuw klant- of orderformulier af te sluiten zonder de gegevens op te slaan.

  4. Als u wijzigingen rechtstreeks aan bestaande klanten en orders wilt aanbrengen, gebruikt u de tekstvakken die deze wijzigingen automatisch naar het model schrijven.