Partager via


Créer une application de données de base avec WPF et Entity Framework 6

Remarque

Si vous exécutez Visual Studio 2022, veillez à utiliser la version 17.3 ou ultérieure de ce didacticiel.

Ce tutoriel montre comment créer un formulaire de base sur une application de données dans Visual Studio. L’application utilise SQL Server LocalDB, la base de données Northwind, Entity Framework 6 (et non Entity Framework Core) et Windows Presentation Foundation (WPF) pour .NET Framework (pas .NET Core ou .NET 5 ou version ultérieure). Il montre comment effectuer une liaison de données de base avec une vue maître-détail et inclut un contrôle BindingNavigator personnalisé avec des boutons qui effectuent les opérations suivantes : aller au premier, déplacer au précédent, aller au suivant, aller au dernier, supprimer, ajouter, définir un nouvel ordre, mettre à jour et annuler.

Ce tutoriel se concentre sur l’utilisation d’outils de données dans Visual Studio et ne tente pas d’expliquer les technologies sous-jacentes en profondeur. Il part du principe que vous connaissez de base le langage XAML (Extensible Application Markup Language), Entity Framework et SQL. Bien que le code de ce didacticiel ne montre pas l’architecture modèle-View-ViewModel (MVVM), qui est standard pour les applications WPF, vous pouvez copier le code avec quelques modifications dans votre propre application MVVM.

Pour afficher le code final de ce didacticiel, consultez les exemples de tutoriels Visual Studio - EF6.

Dans ce tutoriel, vous allez :

  • Installer et se connecter à Northwind
  • Configurer le projet d’application WPF
  • Créer le modèle de données d’entité ADO.NET
  • Les données lient le modèle à la page XAML
  • Ajuster la conception de la page et ajouter des grilles
  • Ajouter des boutons pour naviguer, ajouter, mettre à jour et supprimer
  • Exécuter l’application WPF

Conditions préalables

  • Visual Studio avec la charge de travail développement .NET Desktop installée et le composant Windows Communication Foundation installé. Pour l’installer :

    1. Ouvrez l’application Visual Studio Installer ou sélectionnez Outils>Obtenir des outils et fonctionnalités dans le menu Visual Studio.
    2. Dans Visual Studio Installer, choisissez Modifier en regard de la version de Visual Studio que vous souhaitez modifier.
    3. Sélectionnez l’onglet Composants individuels , puis choisissez Windows Communication Foundation sous Activités de développement.
    4. Sélectionnez Modifier.
  • SQL Server Express LocalDB. Si vous n'avez pas SQL Server Express LocalDB, vous pouvez l’installer à partir de la page de téléchargement SQL Server . Vous pouvez également l’installer avec l’application Visual Studio Installer en tant que composant individuel.

  • Explorateur d’objets SQL Server. Pour l’installer, installez la charge de travail de stockage et de traitement des données dans l’application Visual Studio Installer .

  • Entity Framework 6 Tools. Cela est généralement installé lorsque vous installez la charge de travail de développement .NET Desktop.

Installer et se connecter à Northwind

L’exemple suivant utilise SQL Server Express LocalDB et l’exemple de base de données Northwind. Si le fournisseur de données ADO.NET pour ce produit prend en charge Entity Framework, il doit également utiliser d’autres produits de base de données SQL.

Installez l’exemple de base de données Northwind en procédant comme suit :

  1. Dans Visual Studio, ouvrez la fenêtre explorateur d’objets SQL Server dans le menu Affichage . Développez le nœud SQL Server . Cliquez avec le bouton droit sur votre instance LocalDB, puis sélectionnez Nouvelle requête.

    Une fenêtre de l’éditeur de requête s’ouvre.

  2. Copiez le script Northwind Transact-SQL (T-SQL) dans votre Presse-papiers.

  3. Collez le script T-SQL dans l’éditeur de requête, puis choisissez Exécuter.

    La requête de script T-SQL crée la base de données Northwind et la remplit avec des données.

  4. Ajoutez de nouvelles connexions pour la base de données Northwind.

Configurer le projet d’application WPF

Pour configurer le projet d’application WPF, procédez comme suit :

  1. Dans Visual Studio, créez un projet application WPF (.NET Framework) C#.

  2. Ajoutez le package NuGet pour Entity Framework 6. Dans Explorateur de solutions, sélectionnez le nœud du projet. Dans le menu principal, choisissez Project>Gérer les packages NuGet.

  3. Dans le Gestionnaire de package NuGet, sélectionnez le lien Parcourir . Recherchez et sélectionnez le package EntityFramework . Sélectionnez Installer dans le volet droit et suivez les invites.

    La fenêtre Sortie affiche la progression et vous avertit lorsque l’installation est terminée.

    Capture d’écran montrant le package NuGet Entity Framework.

Vous pouvez maintenant utiliser Visual Studio pour créer un modèle basé sur la base de données Northwind.

Créer le modèle de données d’entité ADO.NET

Pour créer le modèle de données d’entité ADO.NET, procédez comme suit :

  1. Cliquez avec le bouton droit sur le nœud du projet d’application WPF dans l’Explorateur de solutions , puis choisissez Ajouter>un nouvel élément. Dans le volet gauche, sous le nœud C#, choisissez Données et, dans le volet central, choisissez Modèle de données d'entité ADO.NET.

    Capture d’écran montrant la fenêtre Ajouter un nouvel élément avec ADO.NET modèle de données d’entité sélectionné.

  2. Entrez Northwind_model pour le nom, puis choisissez Ajouter.

  3. Dans l’Assistant Modèle de données d’entité, choisissez EF Designer dans la base de données, puis sélectionnez Suivant.

    Capture d’écran montrant le Concepteur EF à partir de la base de données sélectionnée dans l’Assistant de modèle de données d’entité.

  4. Dans Choisir votre connexion de données, sélectionnez votre connexion LocalDB Northwind (par exemple , (localdb)\MSSQLLocalDB), puis sélectionnez Suivant.

  5. Si vous ne voyez pas de connexion :

    1. Choisissez Nouvelle connexion. Si Microsoft SQL Server n’est pas sélectionné comme source de données dans la boîte de dialogue Propriétés de connexion, sélectionnez Modifier. Dans la boîte de dialogue Choisir une source de données , choisissez Microsoft SQL Server, puis sélectionnez OK.

    2. Dans la boîte de dialogue Propriétés de connexion , entrez (localdb)\MSSQLLocalDB comme nom du serveur.

    3. Pour Sélectionner ou entrer un nom de base de données, sélectionnez Northwind, puis ok.

    4. Dans Choisir votre connexion de données, sélectionnez votre connexion Northwind LocalDB, puis sélectionnez Suivant.

  6. Si vous y êtes invité, choisissez la version d’Entity Framework que vous utilisez, puis sélectionnez Suivant.

    Capture d’écran montrant les choix de version pour Entity Framework.

  7. Dans la page suivante de l’Assistant, choisissez les tables, procédures stockées et autres objets de base de données à inclure dans le modèle Entity Framework. Développez le nœud dbo sous le nœud Tables dans l’arborescence. Sélectionnez Clients, Détails de la commande et Commandes. Laissez les valeurs par défaut activées et sélectionnez Terminer.

    Capture d’écran montrant les objets de base de données sélectionnés du modèle de données.

    L’Assistant génère les classes C# qui représentent le modèle Entity Framework, et qui servent de liaison de données entre Visual Studio et l’interface utilisateur WPF. Il crée les fichiers suivants dans votre projet :

    • Le fichier .edmx décrit les relations et d’autres métadonnées qui associent les classes aux objets de la base de données.

    • Les fichiers .tt sont des modèles T4 qui génèrent le code qui fonctionne sur le modèle et enregistre les modifications apportées à la base de données.

    Ces fichiers sont visibles dans l’Explorateur de solutions sous le nœud Northwind_model :

    Capture d’écran montrant les fichiers de modèle Entity Framework de l’Explorateur de solutions.

    Le Concepteur de formulaires pour le .edmx fichier n’est pas utilisé dans ce didacticiel, mais vous pouvez l’utiliser pour modifier certaines propriétés et relations dans le modèle.

Les .tt fichiers sont à usage général et vous devez modifier l’un d’eux pour utiliser la liaison de données WPF, ce qui nécessite ObservableCollection des objets. Suivez ces étapes :

  1. Dans l’Explorateur de solutions, développez le nœud Northwind_model jusqu’à ce que vous trouviez Northwind_model.tt. Double-cliquez sur ce fichier et apportez les modifications suivantes :

  2. Appuyez sur F5 pour générer et exécuter le projet. Lorsque l’application s’exécute pour la première fois, les classes de modèle sont visibles par l’Assistant Sources de données.

Vous êtes maintenant prêt à raccorder ce modèle à la page XAML pour pouvoir afficher, naviguer et modifier les données.

Les données lient le modèle à la page XAML

Bien qu’il soit possible d’écrire votre propre code de liaison de données, il est plus facile de laisser Visual Studio le faire pour vous. Pour ce faire, procédez comme suit :

  1. Dans le menu principal, choisissez Project>Ajouter une nouvelle source de données pour afficher l’Assistant Configuration de la source de données. Comme vous liez aux classes de modèle, et non à la base de données, choisissez Object. Cliquez sur Suivant.

    Capture d’écran montrant l’Assistant Configuration de source de données avec l’objet sélectionné comme source de données.

  2. Développez le nœud de votre projet, sélectionnez l’objet Customer , puis sélectionnez Terminer. Les sources des objets Order sont générées automatiquement à partir de la propriété de navigation Commandes dans Customer.

    Capture d’écran montrant l’objet Customer sélectionné comme source de données.

  3. Dans l’Explorateur de solutions, double-cliquez sur MainWindow.xaml dans votre projet pour modifier le code XAML. Modifiez le Title de MainWindow en quelque chose de plus descriptif, et augmentez ses Height et Width à 600 et 800 (vous pouvez modifier ces valeurs ultérieurement, si nécessaire).

  4. Ajoutez ces trois définitions de ligne à la grille principale, une ligne pour les boutons de navigation, une pour les détails du client et une pour la grille qui affiche leurs commandes :

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

Ensuite, vous affichez chaque propriété dans la Customers classe dans sa propre zone de texte individuelle. Suivez ces étapes :

  1. Dans l’Explorateur de solutions, double-cliquez sur MainWindow.xaml pour l’ouvrir dans le concepteur.

    L’onglet Sources de données s’affiche dans le volet gauche de Visual Studio près de la boîte à outils.

  2. Pour ouvrir la fenêtre Sources de données, sélectionnez l’onglet Sources de données ou sélectionnez Afficher>d’autressources de> Windows dans le menu.

  3. Dans Sources de données, sélectionnez Clients, puis sélectionnez Détails dans la liste déroulante.

  4. Faites glisser le nœud sur la ligne du milieu de la zone de conception. Si vous la placez mal, vous pouvez ultérieurement spécifier la ligne manuellement dans le code XAML en sélectionnant Grid.Row="1".

    Par défaut, les contrôles sont placés verticalement dans un élément de grille, mais vous pouvez les organiser comme vous le souhaitez sur le formulaire. Par exemple, vous pouvez placer la zone de texte Nom au-dessus de l’adresse. L’exemple d’application de ce didacticiel réorganise les champs et les réorganise en deux colonnes.

    Capture d’écran montrant la liaison de la source de données 'Customers' à des contrôles individuels.

    Dans la vue XAML, vous pouvez maintenant voir un nouvel Grid élément dans la ligne 1 (ligne centrale) de la grille parente. La grille parente a un DataContext attribut qui fait référence à un CollectionViewSource élément qui appartient à l’élément Windows.Resources . Étant donné le contexte de données, lorsque la première zone de texte est liée à Address, ce nom est mappé à la propriété Address dans l'objet actif Customer dans CollectionViewSource.

    <Grid DataContext="{StaticResource customerViewSource}">
    
  5. Faites glisser la propriété de l'objet Order de la classe Customers vers la moitié inférieure du formulaire, afin que le concepteur la place sur la ligne 2.

    Lorsqu’un client est visible dans la moitié supérieure du formulaire, vous souhaitez voir leurs commandes dans la moitié inférieure. Vous affichez les Ordres dans un seul contrôle d’affichage de grille. Pour que la liaison de données maître-détail fonctionne comme prévu, il est important d'effectuer une liaison à la propriété Orders dans la classe Customers, et non au nœud distinct Orders.

    Capture d’écran montrant les classes Ordres déplacées et déposées sous forme de grille.

    Visual Studio génère désormais tout le code de liaison qui connecte les contrôles d’interface utilisateur aux événements du modèle.

  6. Pour afficher certaines données, écrivez du code pour remplir le modèle. Accédez à MainWindow.xaml.cs et ajoutez un membre de données pour le contexte de données à la classe MainWindow.

    Cet objet, qui a été généré pour vous, agit comme un contrôle qui effectue le suivi des modifications et des événements dans le modèle.

  7. Ajoutez des membres de données CollectionViewSource pour les clients et les commandes, et ajoutez la logique d'initialisation de constructeur associée au constructeur existant MainWindow(). La première partie de la classe doit ressembler à ceci :

    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. S’il n’y en a pas, ajoutez une directive using pour System.Data.Entity amener la méthode d’extension Load dans l'étendue :

    using System.Data.Entity;
    
  9. Faites défiler vers le bas et recherchez le gestionnaire d’événements Window_Loaded . Notez que Visual Studio a ajouté un CollectionViewSource objet. Cet objet représente l’objet NorthwindEntities que vous avez sélectionné lors de la création du modèle. Comme vous l’avez déjà ajouté, vous n’en avez pas besoin ici. Remplacez le code dans Window_Loaded pour que la méthode ressemble à ceci :

    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. Appuyez sur F5.

    Vous devez voir les détails du premier client qui a été récupéré dans l'objet CollectionViewSource et leurs commandes dans la grille de données. Vous allez corriger la mise en forme dans la section suivante. Vous pouvez également créer un moyen d'afficher les autres enregistrements et d'effectuer les opérations CRUD basiques.

Ajuster la conception de la page et ajouter des grilles pour les nouveaux clients et commandes

La disposition par défaut produite par Visual Studio n’est pas idéale pour votre application. Nous fournissons donc ici le code XAML final à copier dans votre code. Vous avez également besoin de grilles pour permettre à l’utilisateur d’ajouter un nouveau client ou une commande.

Pour ajouter un nouveau client et une commande, créez un ensemble distinct de zones de texte qui ne sont pas liées aux données .CollectionViewSource Vous contrôlez la grille que l’utilisateur voit à tout moment en définissant la propriété Visible dans les méthodes du gestionnaire. Enfin, vous ajoutez un bouton Supprimer à chaque ligne de la grille Commandes pour permettre à l’utilisateur de supprimer une commande individuelle.

  1. Ouvrez MainWindow.xaml et ajoutez les styles suivants à l’élément Windows.Resources :

    <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. Remplacez l’intégralité de la grille externe par ce balisage :

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

Ajouter des boutons pour naviguer, ajouter, mettre à jour et supprimer

Dans une application Windows Forms, vous êtes fourni avec un BindingNavigator objet avec des boutons permettant de parcourir les lignes d’une base de données et d’effectuer des opérations CRUD de base. Même si WPF ne fournit pas de BindingNavigator, il est facile de le créer en ajoutant des boutons à l'intérieur d'une disposition horizontale StackPanel et en associant les boutons à des commandes liées à des méthodes dans le fichier code-behind.

Il existe quatre parties à la logique de commande :

  • Commandes
  • Liaisons
  • Boutons
  • Gestionnaires de commandes dans le code-behind

Ajouter des commandes, des liaisons et des boutons en XAML

  1. Dans le MainWindow.xaml fichier, ajoutez les commandes à l’intérieur de l’élément Windows.Resources comme suit :

    <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. Un CommandBinding associe un événement RoutedUICommand à une méthode dans le code derrière. Ajoutez cet CommandBindings élément après la Windows.Resources balise de fermeture comme suit :

    <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. Ajoutez le bouton StackPanel avec les boutons de navigation, d’ajout, de suppression et de mise à jour. Ajoutez ce style à 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. Collez ce code juste après RowDefinitions pour l'élément extérieur Grid, près du haut de la page XAML :

     <StackPanel Orientation="Horizontal" Margin="2,2,2,0" Height="36" VerticalAlignment="Top" Background="Gainsboro" DataContext="{StaticResource customerViewSource}" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin">
         <Button Name="btnFirst" Content="|◄" Command="{StaticResource FirstCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnPrev" Content="◄" Command="{StaticResource PreviousCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnNext" Content="►" Command="{StaticResource NextCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnLast" Content="►|" Command="{StaticResource LastCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnDelete" Content="Delete Customer" Command="{StaticResource DeleteCustomerCommand}" FontSize="11" Width="120" Style="{StaticResource NavButton}"/>
         <Button Name="btnAdd" Content="New Customer" Command="{StaticResource AddCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="New Order" Name="btnNewOrder" FontSize="11" Width="80" Style="{StaticResource NavButton}" Click="NewOrder_click"/>
         <Button Name="btnUpdate" Content="Commit" Command="{StaticResource UpdateCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="Cancel" Name="btnCancel" Command="{StaticResource CancelCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
     </StackPanel>
    

Ajouter des gestionnaires de commandes à la classe MainWindow

Le code-behind dans MainWindow.xaml.cs est minimal, à l'exception des méthodes d'ajout et de suppression :

  1. Pour naviguer, utilisez les méthodes de la propriété View du CollectionViewSource.

  2. Pour effectuer une suppression en cascade sur une commande, utilisez le DeleteOrderCommandHandler, comme indiqué dans l'exemple de code. Toutefois, vous devez d’abord supprimer l’ordre associé Order_Details.

  3. Utilisez l’option UpdateCommandHandler pour ajouter un client ou une commande à la collection, ou mettre à jour un client ou une commande existante avec les modifications apportées par l’utilisateur dans les zones de texte.

  4. Ajoutez ces méthodes de gestionnaire à la MainWindow classe dans MainWindow.xaml.cs. Si votre CollectionViewSource table des clients porte un nom différent, vous devez alors ajuster ce nom dans chacune des méthodes :

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

Exécuter l’application WPF

  1. Pour démarrer le débogage, appuyez sur F5 . Vérifiez que les données client et commande sont renseignées dans la grille et que les boutons de navigation fonctionnent comme prévu.

  2. Sélectionnez Valider pour ajouter un nouveau client ou une commande au modèle après avoir entré les données.

  3. Sélectionnez Annuler pour annuler le retour d’un nouveau client ou d’un nouveau formulaire de commande sans enregistrer les données.

  4. Pour apporter des modifications directement aux clients et commandes existants, utilisez les zones de texte, qui écrivent automatiquement ces modifications dans le modèle.