25 Liaisons de données Plusieurs composants visuels (zone d’édition, boîte de liste, etc.) présentent une propriété DataSource qui permet d’associer directement une source de données au composant. Les données peuvent provenir d’une base de données (résultat d’une requête SQL par exemple), d’un tableau ou d’un conteneur. La technique de liaison de données permet d’associer une source de données à un composant ce qui consiste à introduire automatiquement ces données dans le composant. Il est certes possible de lire une base de données comme nous l’avons fait au chapitre 24 et initialiser les composants visuels à partir de ces données (propriétés Text ou Items selon le composant) mais les techniques de liaison de données présentent de nombreux avantages et notamment celui de leur caractère automatique. Ce chapitre sera aussi l’occasion d’introduire les grilles de données. 25.1 Liaison avec boîte de liste À la section 16.1, nous avons appris à charger une boîte de liste en utilisant la méthode Add appliquée à la propriété Items d’une boîte de liste. Il est possible de remplir directement une boîte de liste à partir d’un tableau (la technique s’applique aussi aux boîtes combo). Si lbNoms désigne le nom interne d’une boîte de liste, on peut en effet écrire : string[] tabNoms = {"Gaston", "Jeanne", "Prunelle"}; ..... lbNoms.DataSource = tabNoms; Toutes les chaînes contenues dans le tableau tabNoms sont alors automatiquement insérées dans la boîte de liste. 578 C# et .NET Dans le cas d’un tableau où chaque cellule contient plusieurs champs (cas d’un tableau d’objets), il faut initialiser les propriétés DataSource (pour le nom du tableau) et DisplayMember (pour le nom du champ) : public class Pers { string nom; int age; public Pers(string N, int A) {nom = N; age = A;} public string Nom {get {return nom;}} } ..... Pers[] tp = {new Pers("Gaston", 27), new Pers("Jeanne", 23), new Pers("Prunelle", 35)}; ..... lbNoms.DataSource = tp; lbNoms.DisplayMember = "Nom"; Comme chaque cellule du tableau tp contient plusieurs champs, la propriété DisplayMember de la boîte de liste sert à spécifier le champ à retenir pour l’insertion dans la boîte de liste. Il doit s’agir d’une propriété de la classe. Dans la propriété DataSource, nous aurions pu spécifier un objet conteneur de l’une des classes étudiées au chapitre 4 (par exemple un tableau dynamique ArrayList). Enfin, le contenu de la boîte de liste peut provenir d’une base de données. Par exemple du champ Nom de la table Pers de la base de données Biblio.mdb (ici du répertoire courant de l’application) : using System.Data; using System.Data.OleDb; ..... string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=Biblio.mdb"; string selStr = "SELECT Nom FROM Auteurs "; OleDbDataAdapter oDA = new OleDbDataAdapter(selStr, connStr); DataSet oDS = new DataSet(); oDA.Fill(oDS, "Noms"); lbNoms.DataSource = oDS.Tables[0]; lbNoms.DisplayMember = "Nom"; Dans une application ASP.NET, écrivez (généralement dans Page_Load, la fonction MapPath étant membre de la classe Page, voir le chapitre 28) : connStr = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + MapPath("Biblio.mdb"); Liaisons de données CHAPITRE 25 579 25.2 Liaison avec zone d’édition Des données en provenance d’une base de données (ici en provenance de la table Auteurs de la base de données Biblio créée à la section 24.2) peuvent être associées à des zones d’édition ou d’affichage et il est possible d’assurer une navigation dans la base de données (les boutons < et > sont prévus à cet effet). Le programme affiche dans les zones d’édition des données en provenance des champs Nom, AnnéeNaiss et AnnéeMort de la table Auteurs de la base de données Biblio. Chaque clic sur un bouton fait afficher la donnée suivante (bouton >) ou la précédente (bouton <). La fenêtre comprend trois zones d’édition (voir figure 25-1) : zeNom, zeAnnéeNaiss et zeAnnéeMort. Figure 25-1 La boîte de liste (lbNoms) contient des noms d’auteurs et a été initialisée comme nous venons de l’apprendre. La boîte de liste est automatiquement synchronisée avec les contenus des zones d’édition. Nous ne répéterons pas les opérations de création du dataset (voir le chapitre 24). Pour effectuer la liaison entre les champs du dataset et les composants visuels (ici trois zones d’édition), il faut utiliser la propriété DataBindings du composant visuel et lui ajouter les liaisons à effectuer : zeNom.DataBindings.Add("Text", oDS.Tables[0], "Nom"); zeAnnéeNaiss.DataBindings.Add("Text", oDS.Tables[0], "AnnéeNaiss"); zeAnnéeMort.DataBindings.Add("Text", oDS.Tables[0], "AnnéeMort"); La méthode Add appliquée à la propriété DataBindings du composant visuel prend trois arguments : • la propriété du composant visuel à initialiser avec le champ de la donnée (propriété Text puisqu’il s’agit d’une zone d’édition mais il pourrait s’agir de n’importe quelle autre propriété) ; • la table dans le dataset ; • le nom du champ dans cette table du dataset. 580 C# et .NET Pour assurer la navigation dans le dataset (suite à des clics sur les boutons étiquetés < et >), il faut : 1. Déclarer une variable de type CurrencyManager, par exemple CurrencyManager current; en champ public de la classe de la fenêtre. 2. Initialiser cette variable pour signaler que current va contrôler le déplacement dans la table : current = (CurrencyManager)BindingContext(oDS.Tables[0]); 3. Incrémenter ou décrémenter le champ Position de la variable de type CurrencyManager : current.Position++; pour afficher dans les zones d’édition les données de la ligne suivante du dataset. Dépasser la limite du nombre d’enregistrements (par exemple en cliquant sur > alors que l’on est déjà sur le dernier enregistrement) n’a aucun effet. Il en va de même pour un clic sur < alors que l’on est déjà positionné sur le premier enregistrement. L’objet current contient le champ Count qui indique le nombre de lignes dans le dataset. On pourrait tester (dans la fonction qui traite le clic sur le bouton >) : if (current.Position < current.Count-1) current.Position++; mais cela n’est même pas nécessaire puisque nous venons de voir qu’il n’y a aucun danger à dépasser les limites. La navigation par < et > dépend de la clause ORDER BY éventuellement spécifiée dans la commande SQL de sélection (en l’absence de cette clause, il s’agit de l’ordre d’insertion dans le dataset). 25.3 Liaison avec grille de données La boîte à outils comprend l’outil DataGrid qui permet d’afficher des données sous forme tabulaire. Si dg est le nom interne de l’objet DataGrid (propriété Name), oDS le nom du dataset (voir le chapitre 24) et Auteurs le nom d’une table dans ce dataset, on associe la grille aux données du dataset en initialisant les propriétés de la grille : • DataSource avec le nom du dataset ; • DataMember avec le nom de la table dans le dataset. Par exemple : dg.DataSource = oDS; dg.DataMember = "Auteurs"; La grille est alors automatiquement arrangée et les données provenant de la table dans le dataset sont affichées dans la grille (voir figure 25-2). Liaisons de données CHAPITRE 25 581 Figure 25-2 Les données d’une grille peuvent provenir d’un dataset créé en mémoire : DataTable dt; DataColumn dc; ..... dt = new DataTable("Pers"); // créer la table (objet DataTable) // spécifier les colonnes de la table (nom + âge) dt.Columns.Add("Nom", typeof(string)); dt.Columns.Add("Age", typeof(int)); // remplir la table de données dt.Rows.Add(new Object[] {"Loulou", 33}); dt.Rows.Add(new Object[] {"Chouchou", 30}); dg.DataSource = dt; Il n’est alors pas nécessaire d’initialiser la propriété DataMember de la grille car la propriété DataSource a été initialisée avec un objet DataTable et celui-ci est limité à une seule table (alors qu’un objet DataSet peut contenir plusieurs tables). On pourrait écrire de manière tout à fait équivalente (il serait alors possible de spécifier d’autre caractéristiques de colonne comme AllowNull, AutoIncrement, Caption, DefaultValue, ReadOnly et Unique, sans oublier que PrimaryKey est spécifié au niveau de la table) : dt = new DataTable("Pers"); DataColum dc = new DataColumn("Nom", typeof(string)); dt.Columns.Add(dc); dc = new DataColumn("Age", typeof(int)); dt.Columns.Add(dc); DataRow dr = dt.NewRow(); dr["Nom"] = "Loulou"; dr["Age"] = 33; dt.Rows.Add(dr); dr = dt.NewRow(); dr["Nom"] = "Chouchou"; dr["Age"] = 30; dt.Rows.Add(dr); dg.DataSource = dt; Au lieu d’initialiser la propriété DataSource, on peut écrire : dg.SetDataBinding(dt, null); Jusqu’à présent, notre grille affiche toutes les colonnes de la table et a un look tout à fait standard. Nous allons améliorer cela progressivement. 582 C# et .NET Intéressons-nous pour cela aux propriétés de la classe DataGrid. Les propriétés de l’objet DataGrid sont nombreuses (voir figure 25-3). Elles permettent de personnaliser la grille : titre de la grille, couleur d’arrière-plan, police d’affichage, etc. Figure 25-3 Cliquer sur le lien Mise en forme automatique vous donne la possibilité de choisir parmi plusieurs types de présentation de grilles. Vous trouverez ci-après les apparences générales pour les présentations dites par défaut, professionnelles, classiques, simples et 256 couleurs : Personnalisation des grilles (Mise en forme automatique), figures 25-4 à 25-9. Figure 25-4 Figure 25-5 Liaisons de données CHAPITRE 25 583 Personnalisation des grilles (Mise en forme automatique), figures 25-4 à 25-9. Figure 25-6 Figure 25-7 Figure 25-8 Figure 25-9 Plutôt que des manipulations de Visual Studio, envisageons des opérations par programme. Elles nous feront découvrir des propriétés de l’objet DataGrid. Certaines propriétés s’appliquent à la grille dans son ensemble. Le nom de ces propriétés indique clairement la fonction correspondante : AllowSorting (true par défaut), AlternatingBackColor, BackColor, BackgroundColor, BackgroundImage, BorderStyle, CaptionBackColor, CaptionFont, CaptionForeColor, CaptionText, CaptionVisible, ColumnHeadersVisible, Font, ForeColor, GridLineColor, GridLineStyle, HeaderBackColor, HeaderFont, HeaderForeColor, SelectionBackColor et SelectionForeColor. Si la propriété AllowSorting vaut true, un clic sur un en-tête de colonne provoque un tri, alternativement de manière croissante et décroissante. Par défaut, toutes les colonnes de la table sont affichées. Pour personnaliser les colonnes, il faut créer une collection d’objets DataGridTableStyle. Cette collection est accessible via la propriété TableStyles de la grille. DataGridTableStyle est une classe qui représente les colonnes affichées. Un objet DataGridTableStyle doit être 584 C# et .NET associé à une table (par exemple la table Auteurs de la base de données Librairie.mdb). En créant plusieurs objets DataGridTableStyle et en modifiant la propriété TableStyles (de type DataGridTableStyle) de la grille, on modifie complètement le contenu de la grille. Mais aussi son apparence car la classe DataGridTableStyle contient également les propriétés AllowSorting, AlternatingBackColor, BackColor, ForeColor, GridLineColor, GridLineStyle, HeaderBackColor, HeaderFont, HeaderForeColor, RowHeaderVisible, SelectionBackColor et SelectionForeColor. Dans l’exemple qui suit, nous ne reprenons que les colonnes Nom et Prénom de la table Auteurs : DataGridTableStyle dgts = new DataGridTableStyle(); dgts.MappingName = "Auteurs"; // nom de la table DataGridColumnStyle dgcs = new DataGridTextBoxColumn(); dgcs.MappingName = "Nom"; // nom du champ dgcs.HeaderText = "NOM"; // libellé de l’en-tête de colonne dgcs.Width = 200; // largeur de la colonne dgts.GridColumnStyles.Add(dgcs); dgcs = new DataGridTextBoxColumn(); // ajouter le champ Prénom dgcs.MappingName = "Prénom"; dgcs.HeaderText = "Prénom"; dgcs.Width = 150; dgts.GridColumnStyles.Add(dgcs); dg.TableStyles.Add(dgts); D’autres propriétés de DataGridColumnStyle sont : Alignment avec ses valeurs Left, Center et Right de l’énumération HorizontalAlignment. NullText texte à afficher quand le contenu du champ est nul. ReadOnly indique si le champ est en lecture seule. Pour afficher une colonne de type date (champ DN de la table) dans laquelle on affiche le mois (en abrégé) suivi de l’année, on écrit : dgcs = new DataGridTextBoxColumn(); dgcs.MappingName = "DN"; dgcs.HeaderText = "Paru en"; dgcs.Format = "MMM yyyy"; dgcs.Width = 150; dgts.GridColumnStyles.Add(dgcs); 25.4 Création de formulaires de données avec Visual Studio Visual Studio.NET permet de créer des formulaires de données. Nous allons suivre les différentes étapes de cette création. Pour insérer un formulaire de données dans le projet de l’application, activez le menu (voir figure 25-10) Projet → Ajouter un nouvel élément. Sélectionnez Assistant de formulaires de données. Une nouvelle classe de fenêtre est créée. Par défaut, elle s’appelle DataForm1. Liaisons de données CHAPITRE 25 Figure 25-10 Un assistant va vous aider dans tout le processus de création (voir figure 25-11). Figure 25-11 Commencez par donner un nom au dataset qui va être créé (voir figure 25-12). 585 586 C# et .NET Figure 25-12 L’assistant va vous réclamer des informations concernant la base de données (voir figure 25-13). D’abord le pilote (il s’agit ici d’une base de données Access). Figure 25-13 Liaisons de données CHAPITRE 25 587 Spécifiez ensuite le nom de la base de données. Si la base de données doit se trouver dans le répertoire de l’application, ne gardez que le nom relatif (voir figure 25-14). Figure 25-14 Spécifiez enfin les modes d’accès et de partage (voir figure 25-15). Figure 25-15 588 C# et .NET Spécifiez les tables utilisées (voir figure 25-16). Figure 25-16 Puis les champs à retenir dans chaque table (voir figure 25-17). Figure 25-17 Liaisons de données CHAPITRE 25 589 On spécifie ici la présentation générale : grille ou formulaire ainsi que les opérations susceptibles d’être effectuées : ajout, suppression, annulation et navigation (voir figure 25-18). Figure 25-18 Visual Studio.NET donne la présentation générale de la fenêtre. Vous pouvez retravailler entièrement cette présentation et modifier toutes les propriétés. Vous pourriez aussi éliminer le bouton Charger et placer le code de cette fonction dans le constructeur de la classe DataForm1, après le commentaire TODO (voir figure 25-19). Figure 25-19 590 C# et .NET Pour que cette fenêtre soit affichée au démarrage de l’application, modifiez Main dans Form1.cs comme suit (remplacer Form1 par DataForm1) : static void Main() { Application.Run(new DataForm1()); }