Fermer

avril 12, 2023

Comment remplir WinForms RadGridView avec des données en mode lié

Comment remplir WinForms RadGridView avec des données en mode lié


La grille est le composant de données le plus couramment utilisé dans l’interface utilisateur Telerik pour WinForms. Découvrez comment analyser différents fichiers ou formats de texte pouvant être utilisés pour le transfert de données.

Le progrès Interface utilisateur Telerik pour WinForms fournit une grande diversité de contrôles d’interface utilisateur pour visualiser tout type de données. Sans aucun doute, RadGridView est le composant de données le plus couramment utilisé, et nous ne pouvons pas imaginer une application utilisateur final sans au moins une grille. C’est pourquoi nous avons décidé de lancer une série d’articles de blog présentant le puissant arsenal offert par RadGridView.

Plongeons-nous dans l’océan profond de la liaison de données en tant qu’approche majeure pour remplir la grille avec des enregistrements.

Les enregistrements de données sont généralement stockés sur le serveur ou dans un fichier. Un tutoriel étape par étape sur la façon de se lier à un serveur local peut être trouvé sur le lien suivant : Comment lier GridView à un serveur SQL local. Différentes collections d’objets personnalisés sont également prises en charge. (En savoir plus ici.)

Nous nous concentrerons sur l’analyse de différents fichiers ou formats de texte pouvant être utilisés pour le transfert de données. Cependant, commençons par le cas générique avec une liaison à un Tableau de données.

Lier à DataTable

Le moyen le plus simple de lier certaines données à RadGridView est de créer un DataTable, de définir des colonnes avec le type respectif et d’ajouter des lignes avec le contenu des cellules :

private void BindToDataTable()
{
  DataTable dt = new DataTable();
  dt.Columns.Add("Id", typeof(int));
  dt.Columns.Add("Name", typeof(string));
  dt.Columns.Add("CreatedOn", typeof(DateTime));
  for (int i = 0; i < 10; i++)
  {
    dt.Rows.Add(i, "Item"+i, DateTime.Now.AddDays(i));
  }

  this.radGridView1.DataSource = dt;
}

Table de données WinForms

Lier à JSON

Liaison directe à JSON n’est pas pris en charge par RadGridView. Cependant, il est possible de convertir le contenu JSON en un DataTable, puis de définir le DataTable analysé comme La source de données pour le contrôle RadGridView.

Remarque : Il est nécessaire d’ajouter une référence à Json.NET:

Le gestionnaire de références montre que Json.NET, System.Drawing et System.Windows.Forms sont tous sélectionnés

public partial class RadForm1 : Telerik.WinControls.UI.RadForm
{
  public RadForm1()
  {
    InitializeComponent();
    BindToJson();
    this.radGridView1.BestFitColumns();
  }
  private void BindToJson()
  {
    string json = @"[
      {""id"":""10"",""name"":""User"",""add"":false,""edit"":true,""authorize"":true,""view"":true},
      { ""id"":""11"",""name"":""Group"",""add"":true,""edit"":false,""authorize"":false,""view"":true},
      { ""id"":""12"",""name"":""Permission"",""add"":true,""edit"":true,""authorize"":true,""view"":true}
    ]";
    DataTable table = Newtonsoft.Json.JsonConvert.DeserializeObject<DataTable>(json);
    this.radGridView1.DataSource = table;
  }
}

Liaison de la table de données aux données JSON

Lier au CSV

Les fichiers de valeurs séparées par des virgules (CSV) sont un moyen simple de stocker une énorme quantité de contenu de données enregistré dans un format structuré en tableau. Il peut également être facilement analysé dans un DataTable et donc réutilisé comme collection DataSource pour la grille.

Données CSV

public RadForm1()
{
  InitializeComponent();
  BindToCsv();
}

private void BindToCsv()
{
  bool isFirstRowHeader = true;
  string path = @"..\..\sampleData.csv";
  string header = isFirstRowHeader ? "Yes" : "No";
  string pathOnly = System.IO.Path.GetDirectoryName(path);
  string fileName = System.IO.Path.GetFileName(path);
  string sql = @"SELECT * FROM [" + fileName + "]";
  
  using (System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection(
    @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly + 
    ";Extended Properties=\"Text;HDR=" + header + "\""))
      using (System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand(sql, connection))
        using (System.Data.OleDb.OleDbDataAdapter adapter = new System.Data.OleDb.OleDbDataAdapter(command))
        {
          DataTable dataTable = new DataTable();
          dataTable.Locale = CultureInfo.CurrentCulture;
          adapter.Fill(dataTable);
          this.radGridView1.DataSource = dataTable;
        }
}

RadGridView est capable d’afficher des données hiérarchiques où les principaux scénarios peuvent être répertoriés comme ci-dessous :

  • Hiérarchie d’auto-référence
  • Hiérarchie relationnelle objet (détail principal)
  • Chargement à la demande

Lier à la hiérarchie d’auto-référence

Le contenu des données est représenté par une collection plate où la relation hiérarchique est définie par les champs ID et ID du parent. Ainsi, chaque enregistrement connaît l’ID de son parent et la structure imbriquée peut être construite.

private void BindSelfReferenceHierarchy()
{
  DataTable selfRefTable = new DataTable();
  selfRefTable.Columns.Add("Id", typeof(int));
  selfRefTable.Columns.Add("ParentId", typeof(int));
  selfRefTable.Columns.Add("Name", typeof(string));
  selfRefTable.Rows.Add(1, 0, "My Computer");
  selfRefTable.Rows.Add(2, 1, @"C:\");
  selfRefTable.Rows.Add(3, 2, "Program Files");
  selfRefTable.Rows.Add(4, 3, "Microsoft");
  selfRefTable.Rows.Add(5, 3, "Telerik");
  selfRefTable.Rows.Add(6, 2, "WINDOWS");
  selfRefTable.Rows.Add(7, 1, @"D:\");

  this.radGridView1.Relations.AddSelfReference(this.radGridView1.MasterTemplate, "Id", "ParentId");
  this.radGridView1.DataSource = selfRefTable;
}

Niveaux hiérarchiques affichés avec des retraits

Liaison aux données relationnelles objet

Le contenu des données est représenté par deux (ou jusqu’à N selon la profondeur de la hiérarchie) collections plates où chaque niveau hiérarchique a besoin d’une collection de données distincte et d’un modèle de grille pour stocker les données.

Les différents niveaux de grille sont reliés par une relation spécifique, appelée GridViewRelation pointant respectivement vers les niveaux parent et enfant. Il relie un champ du niveau parent et un champ du niveau enfant. C’est assez proche de la clé étrangère dans les tables SQL.

L’extrait de code suivant montre comment construire une hiérarchie objet-relationnelle Catégories-Produits :

private void BindToObjectRelational()
{
  Random rand = new Random();
  DataTable categories = new DataTable();
  categories.Columns.Add("CategoryID", typeof(int));
  categories.Columns.Add("Title", typeof(string));
  categories.Columns.Add("CreatedOn", typeof(DateTime));
  for (int i = 0; i < 5; i++)
  {
    categories.Rows.Add(i, "Master" + i, DateTime.Now.AddDays(i));
  }

  DataTable productsTable = new DataTable();
  productsTable.Columns.Add("ProductID", typeof(int));
  productsTable.Columns.Add("CategoryID", typeof(int));
  productsTable.Columns.Add("Name", typeof(string));
  productsTable.Columns.Add("UnitPrice", typeof(decimal));
  for (int i = 0; i < 30; i++)
  {
    productsTable.Rows.Add(i, rand.Next(0, 5), "Product" + i, 1.25 * i);
  }

  this.radGridView1.MasterTemplate.DataSource = categories;
  this.radGridView1.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;

  GridViewTemplate productsLevel = new GridViewTemplate();
  productsLevel.DataSource = productsTable;
  productsLevel.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
  this.radGridView1.MasterTemplate.Templates.Add(productsLevel);

  GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
  relation.ChildTemplate = productsLevel;
  relation.RelationName = "CategoriesProducts";
  relation.ParentColumnNames.Add("CategoryID");
  relation.ChildColumnNames.Add("CategoryID");
  this.radGridView1.Relations.Add(relation);

}

la ligne de la grille est développée, révélant ainsi la hiérarchie enfant

Plusieurs onglets enfants dans la hiérarchie

Chaque GridViewTemplateGridViewTemplate a un Modèles propriété qui stocke ses niveaux hiérarchiques enfants respectifs. Ainsi, il est possible d’ajouter autant de modèles enfants que possible sur un même niveau hiérarchique.

Maintenant, nous allons ajouter un deuxième onglet, à côté de l’onglet Produits avec les commandes :

DataTable ordersTable = new DataTable();
ordersTable.Columns.Add("OrderID", typeof(int));
ordersTable.Columns.Add("CategoryID", typeof(int)); 
ordersTable.Columns.Add("OrderDate", typeof(DateTime));
for (int i = 0; i < 30; i++)
{
  ordersTable.Rows.Add(i, rand.Next(0, 5), DateTime.Now.AddDays(-1 * i));
}

GridViewTemplate ordersLevel = new GridViewTemplate();
ordersLevel.DataSource = ordersTable;
ordersLevel.Caption = "Orders";
ordersLevel.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
this.radGridView1.MasterTemplate.Templates.Add(ordersLevel);

GridViewRelation relationOrders = new GridViewRelation(radGridView1.MasterTemplate);
relationOrders.ChildTemplate = ordersLevel;
relationOrders.RelationName = "CategoriesOrders";
relationOrders.ParentColumnNames.Add("CategoryID");
relationOrders.ChildColumnNames.Add("CategoryID");
this.radGridView1.Relations.Add(relationOrders);

la ligne de la grille est développée, révélant la hiérarchie enfant, qui comprend des onglets pour les produits et les commandes

Hiérarchie imbriquée à plusieurs niveaux

De la même manière, nous définirons imbriqué GridViewTemplates avec le nécessaire GridViewRelations construire trois niveaux de hiérarchie : Catégories-Produits-Commandes.

Random rand = new Random();
DataTable categories = new DataTable();
categories.Columns.Add("CategoryID", typeof(int));
categories.Columns.Add("Title", typeof(string));
categories.Columns.Add("CreatedOn", typeof(DateTime));
for (int i = 0; i < 5; i++)
{
  categories.Rows.Add(i, "Master" + i, DateTime.Now.AddDays(i));
}

DataTable productsTable = new DataTable();
productsTable.Columns.Add("ProductID", typeof(int));
productsTable.Columns.Add("CategoryID", typeof(int));
productsTable.Columns.Add("Name", typeof(string));
productsTable.Columns.Add("UnitPrice", typeof(decimal));
for (int i = 0; i < 30; i++)
{
  productsTable.Rows.Add(i, rand.Next(0, 5), "Product" + i, 1.25 * i);
}

this.radGridView1.MasterTemplate.DataSource = categories;
this.radGridView1.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;

GridViewTemplate productsLevel = new GridViewTemplate();
productsLevel.DataSource = productsTable;
productsLevel.Caption = "Products";
productsLevel.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
this.radGridView1.MasterTemplate.Templates.Add(productsLevel);

GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = productsLevel;
relation.RelationName = "CategoriesProducts";
relation.ParentColumnNames.Add("CategoryID");
relation.ChildColumnNames.Add("CategoryID");
this.radGridView1.Relations.Add(relation);

DataTable ordersTable = new DataTable();
ordersTable.Columns.Add("OrderID", typeof(int));
ordersTable.Columns.Add("ProductID", typeof(int)); 
ordersTable.Columns.Add("OrderDate", typeof(DateTime));
for (int i = 0; i < 100; i++)
{
  ordersTable.Rows.Add(i, rand.Next(0, 30), DateTime.Now.AddDays(-1 * i));
}

GridViewTemplate ordersLevel = new GridViewTemplate();
ordersLevel.DataSource = ordersTable;
ordersLevel.Caption = "Orders";
ordersLevel.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
productsLevel.Templates.Add(ordersLevel);

GridViewRelation relationOrders = new GridViewRelation(productsLevel);
relationOrders.ChildTemplate = ordersLevel;
relationOrders.RelationName = "ProductsOrders";
relationOrders.ParentColumnNames.Add("ProductID");
relationOrders.ChildColumnNames.Add("ProductID");
this.radGridView1.Relations.Add(relationOrders);

La ligne parent est développée pour afficher la hiérarchie enfant, qui a une ligne développée montrant le niveau imbriqué suivant de la hiérarchie

Charger la hiérarchie à la demande

Dans certains cas, il ne serait pas nécessaire de charger l’intégralité des données pour tous les niveaux hiérarchiques. Il s’agit de la fonctionnalité dite de chargement à la demande. Les niveaux hiérarchiques sont chargés uniquement lorsqu’ils sont demandés, par exemple lorsque l’utilisateur développe une ligne parente.

private void LoadOnDemand()
{
  Random rand = new Random();
  GridViewDecimalColumn idColumn = new GridViewDecimalColumn("CategoryID");
  GridViewTextBoxColumn titleColumn = new GridViewTextBoxColumn("Title");
  GridViewDateTimeColumn dateColumn = new GridViewDateTimeColumn("CreatedOn");
  this.radGridView1.MasterTemplate.Columns.AddRange(idColumn, titleColumn, dateColumn);
  this.radGridView1.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
  for (int i = 0; i < 5; i++)
  {
    this.radGridView1.MasterTemplate.Rows.Add(i, "Master" + i, DateTime.Now.AddDays(i));
  }

  GridViewTemplate productsLevel = new GridViewTemplate();
  productsLevel.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
  GridViewDecimalColumn productIdColumn = new GridViewDecimalColumn("ProductID");
  GridViewDecimalColumn categoryIdColumn = new GridViewDecimalColumn("CategoryID");
  GridViewTextBoxColumn productNameColumn = new GridViewTextBoxColumn("Name");
  GridViewDecimalColumn unitPriceColumn = new GridViewDecimalColumn("UnitPrice");
  productsLevel.Columns.AddRange(productIdColumn, categoryIdColumn, productNameColumn, unitPriceColumn);
  this.radGridView1.MasterTemplate.Templates.Add(productsLevel);
  productsLevel.HierarchyDataProvider = new GridViewEventDataProvider(productsLevel);
  this.radGridView1.RowSourceNeeded += RadGridView1_RowSourceNeeded;
}

private void RadGridView1_RowSourceNeeded(object sender, GridViewRowSourceNeededEventArgs e)
{
  if (e.Template.HierarchyLevel==1)
  {
    for (int i = 0; i < 30; i++)
    {
      GridViewRowInfo row = e.Template.Rows.NewRow();
      row.Cells["ProductID"].Value = i;
      row.Cells["CategoryID"].Value = e.ParentRow.Cells["CategoryID"].Value;
      row.Cells["Name"].Value = "Product" + row.Cells["CategoryID"].Value+"."+i;
      row.Cells["UnitPrice"].Value = 1.25 * i;
      e.SourceCollection.Add(row );
    }
  }
}

Hiérarchie chargée à la demande

Le GridViewRowSourceNeededEventArgs vous donne accès au modèle respectif. Ainsi, si vous avez plusieurs niveaux hiérarchiques, vous pouvez facilement les distinguer via le Template.Niveau hiérarchique ou Légende.

Conversion des types de données

Dans la dernière section de cet article de blog, nous prêterons attention à une question très délicate et importante en ce qui concerne la liaison des données et le mappage des champs des enregistrements de données avec les colonnes de la grille. Nous vous donnerons des conseils sur la façon de gérer les cas où l’enregistrement de données stocke la valeur dans un type spécifique qui n’est pas compatible avec la colonne respective de RadGridView que nous voulons utiliser.

Le cas le plus général consiste à stocker « OUI » et « NON » dans la collection DataSource tandis que GridViewCheckBoxColumn attend des valeurs booléennes pour analyser les valeurs vrai/faux. Considérons la configuration suivante :

DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("IsActive", typeof(string));
for (int i = 0; i < 20; i++)
{
  dt.Rows.Add(i, "Item" + i, i % 2 == 0 ? "YES" : "NO");
}
this.radGridView1.DataSource = dt;

Item3 est en cours de modification dans la colonne IsActive donc il indique NON

Par défaut, RadGridView génère GridViewTextBoxColumn pour les champs de chaîne. Toutefois, si vous souhaitez remplacer la colonne par défaut par un GridViewCheckBoxColumn, vous êtes censé perdre le mappage de la valeur des champs car la valeur de chaîne ne peut pas être analysée en booléen.

Pour gérer ce cas, nous allons implémenter un outil personnalisé TypeConverter classe qui détermine comment RadGridView reconnaît ce type. Pour plus d’informations, voir Comment : implémenter un convertisseur de type dans MSDN.

public class ToggleStateConverter : TypeConverter
{
  public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
  {
    return destinationType == typeof(ToggleState) || destinationType == typeof(bool);
  }
  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
  {
    if (value is string && destinationType == typeof(ToggleState))
    {
      string stringValue = (string)value;
      switch (stringValue)
      {
        case "YES":
          return ToggleState.On;
        case "NO":
          return ToggleState.Off;
        default:
          return ToggleState.Indeterminate;
      }
    }
    else if (value is bool && destinationType == typeof(char))
    {
      bool boolValue = (bool)value;
      switch (boolValue)
      {
        case true:
          return "YES";
        case false:
          return "NO";
        default:
          return "NO";
      }
    }
    return base.ConvertTo(context, culture, value, destinationType);
  }
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    return sourceType == typeof(ToggleState) || sourceType == typeof(bool);
  }
  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
  {
    ToggleState state;
    bool boolValue;
    if (value is ToggleState)
    {
      state = (ToggleState)value;
      switch (state)
      {
        case ToggleState.On:
          return "YES";
        case ToggleState.Off:
          return "NO";
        default:
          return "NO";
      }
    }
    else if (value is bool)
    {
      boolValue = (bool)value;
      switch (boolValue)
      {
        case true:
          return "YES";
        case false:
          return "NO";
        default:
          return "NO";
      }
    }
    return base.ConvertFrom(context, culture, value);
  }
}

Maintenant, nous devrions appliquer le convertisseur à la colonne :

DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("IsActive", typeof(string));
for (int i = 0; i < 20; i++)
{
  dt.Rows.Add(i, "Item" + i, i % 2 == 0 ? "YES" : "NO");
}
this.radGridView1.DataSource = dt;
this.radGridView1.Columns.Remove("IsActive");
GridViewCheckBoxColumn checkBoxColumn = new GridViewCheckBoxColumn("IsActive");
checkBoxColumn.FieldName = "IsActive";
checkBoxColumn.DataTypeConverter = new ToggleStateConverter();
checkBoxColumn.EditMode = EditMode.OnValueChange;
this.radGridView1.Columns.Add(checkBoxColumn);

Maintenant, la colonne IsActive a des cases à cocher

Une approche similaire avec un TypeConverter peut être appliqué à n’importe quelle colonne de la grille et vous pouvez convertir différents types. Un autre exemple est la conversion des valeurs DateTime au format UNIX et vice versa : Comment afficher les valeurs DateTime stockées dans un format UNIX avec le filtrage fonctionnant toujours dans RadGridView.

Restez à l’écoute pour plus! Nous vous verrons la prochaine fois pour en savoir plus sur l’interface utilisateur Telerik pour WinForms Grid.

Inscrivez-vous pour un essai

N’hésitez pas à vous inscrire à notre essai gratuit de 30 jours, qui vous donne accès aux composants ainsi qu’à notre support technique exceptionnel sans frais. Dirigez-vous simplement vers le Interface utilisateur Telerik pour WinForms page de présentation ou cliquez sur le bouton ci-dessous pour vous inscrire à un essai dès aujourd’hui !

Essayez l’interface utilisateur pour WinForms

Temps pour les commentaires

Oh, et encore une chose. Nous serions plus qu’heureux d’entendre vos réflexions et suggestions sur notre bibliothèque, alors s’il vous plaît envoyez-nous une ligne dans la section des commentaires ci-dessous ou en visitant l’interface utilisateur Telerik pour WinForms Portail de commentaires.




Source link