Vues de tableau dans Xamarin.Mac

Cet article traite de l’utilisation des vues de table dans une application Xamarin.Mac. Il décrit la création de vues de table dans Xcode et Interface Builder et l’interaction avec celles-ci dans le code.

Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès aux mêmes vues de table qu’un développeur travaillant dans Objective-C et Xcode . Étant donné que Xamarin.Mac s’intègre directement à Xcode, vous pouvez utiliser le Générateur d’interface de Xcode pour créer et gérer vos vues de table (ou éventuellement les créer directement en code C#).

Un affichage Tableau affiche les données dans un format tabulaire contenant une ou plusieurs colonnes d’informations dans plusieurs lignes. En fonction du type d’affichage table créé, l’utilisateur peut trier par colonne, réorganiser des colonnes, ajouter des colonnes, supprimer des colonnes ou modifier les données contenues dans la table.

Exemple de table

Dans cet article, nous allons aborder les principes fondamentaux de l’utilisation des vues de tableau dans une application Xamarin.Mac. Il est fortement recommandé de commencer par travailler sur l’article Hello, Mac , en particulier les sections Introduction to Xcode et Interface Builderet Outlets and Actions , car il couvre les concepts et techniques clés que nous allons utiliser dans cet article.

Vous pouvez également consulter la section Exposition des classes/méthodes C# du Objective-C document Xamarin.Mac Internals . Elle explique les commandes Register et Export utilisées pour relier vos classes C# à Objective-C des objets et des éléments d’interface utilisateur.

Présentation des vues de table

Un affichage Tableau affiche les données dans un format tabulaire contenant une ou plusieurs colonnes d’informations dans plusieurs lignes. Les affichages de tableau sont affichés à l’intérieur des affichages de défilement (NSScrollView) et, à compter de macOS 10.7, vous pouvez utiliser n’importe quel NSView affichage au lieu de Cellules (NSCell) pour afficher les lignes et les colonnes. Cela dit, vous pouvez toujours utiliser NSCell , mais vous allez généralement sous-classer NSTableCellView et créer vos lignes et colonnes personnalisées.

Un mode Table ne stocke pas ses propres données, mais s’appuie sur une source de données (NSTableViewDataSource) pour fournir à la fois les lignes et les colonnes requises, selon les besoins.

Le comportement d’un affichage table peut être personnalisé en fournissant une sous-classe du délégué d’affichage table (NSTableViewDelegate) pour prendre en charge la gestion des colonnes de table, le type pour sélectionner les fonctionnalités, la sélection et la modification de ligne, le suivi personnalisé et les vues personnalisées pour les colonnes et les lignes individuelles.

Lors de la création d’affichages de tableau, Apple suggère les éléments suivants :

  • Autorisez l’utilisateur à trier la table en cliquant sur un en-tête de colonne.
  • Créez des en-têtes de colonne qui sont des noms ou des expressions substantifs courtes qui décrivent les données affichées dans cette colonne.

Pour plus d’informations, consultez la section Affichages de contenu des Instructions d’Apple relatives à l’interface utilisateur OS X.

Création et gestion des vues de table dans Xcode

Lorsque vous créez une application Xamarin.Mac Cocoa, vous obtenez une fenêtre vide standard par défaut. Cette fenêtre est définie dans un .storyboard fichier automatiquement inclus dans le projet. Pour modifier la conception de vos fenêtres, dans le Explorateur de solutions, double-cliquez sur le Main.storyboard fichier :

Sélection du storyboard main

La conception de fenêtre s’ouvre dans le générateur d’interface de Xcode :

Modification de l’interface utilisateur dans Xcode

Tapez table dans la zone de recherche de l’inspecteur de bibliothèque pour faciliter la recherche des contrôles d’affichage table :

Sélection d’un affichage table dans la bibliothèque

Faites glisser un affichage Tableau sur le contrôleur d’affichage dans l’éditeur d’interface, faites-le remplir la zone de contenu du contrôleur d’affichage et définissez-le sur l’emplacement où il se réduit et s’agrandit avec la fenêtre de l’éditeur de contraintes :

Contraintes d’édition

Sélectionnez l’affichage Table dans la hiérarchie d’interface et les propriétés suivantes sont disponibles dans l’inspecteur d’attributs :

Capture d’écran montrant les propriétés disponibles dans l’inspecteur d’attribut.

  • Mode contenu : vous permet d’utiliser des affichages (NSView) ou des cellules (NSCell) pour afficher les données dans les lignes et les colonnes. À compter de macOS 10.7, vous devez utiliser views.
  • Floats Group Rows : si true, l’affichage Tableau dessine les cellules groupées comme si elles flottaient.
  • Colonnes : définit le nombre de colonnes affichées.
  • En-têtes : si true, les colonnes auront des en-têtes.
  • Réorganisation : si true, l’utilisateur peut faire glisser la réorganisation des colonnes dans la table.
  • Redimensionnement : si true, l’utilisateur peut faire glisser les en-têtes de colonne pour redimensionner les colonnes.
  • Dimensionnement des colonnes : contrôle la façon dont la table dimensionne automatiquement les colonnes.
  • Surbrillance : contrôle le type de mise en surbrillance utilisée par le tableau lorsqu’une cellule est sélectionnée.
  • Autres lignes : si true, une autre ligne aura une couleur d’arrière-plan différente.
  • Grille horizontale : sélectionne le type de bordure dessinée horizontalement entre les cellules.
  • Grille verticale : sélectionne le type de bordure dessinée verticalement entre les cellules.
  • Couleur de grille : définit la couleur de la bordure de cellule.
  • Arrière-plan : définit la couleur d’arrière-plan de la cellule.
  • Sélection : vous permet de contrôler la façon dont l’utilisateur peut sélectionner des cellules dans le tableau comme suit :
    • Multiple : si true, l’utilisateur peut sélectionner plusieurs lignes et colonnes.
    • Colonne : si true, l’utilisateur peut sélectionner des colonnes.
    • Type Sélectionner : si true, l’utilisateur peut taper un caractère pour sélectionner une ligne.
    • Vide : si true, l’utilisateur n’est pas tenu de sélectionner une ligne ou une colonne, la table n’autorise aucune sélection du tout.
  • Enregistrement automatique : nom sous lequel le format des tables est automatiquement enregistré.
  • Informations sur les colonnes : si true, l’ordre et la largeur des colonnes sont automatiquement enregistrés.
  • Sauts de ligne : sélectionnez la façon dont la cellule gère les sauts de ligne.
  • Tronque la dernière ligne visible : si true, la cellule sera tronquée dans les données ne peut pas tenir à l’intérieur de ses limites.

Important

Sauf si vous conservez une application Xamarin.Mac héritée, NSView les vues de table basées doivent être utilisées sur NSCell les vues de table basées. NSCell est considéré comme hérité et peut ne pas être pris en charge à l’avenir.

Sélectionnez une colonne de table dans la hiérarchie d’interface et les propriétés suivantes sont disponibles dans l’inspecteur d’attributs :

Capture d’écran montrant les propriétés disponibles pour une colonne de table dans l’inspecteur d’attribut.

  • Titre : définit le titre de la colonne.
  • Alignement : définissez l’alignement du texte dans les cellules.
  • Police de titre : sélectionne la police du texte d’en-tête de la cellule.
  • Clé de tri : clé utilisée pour trier les données dans la colonne. Laissez vide si l’utilisateur ne peut pas trier cette colonne.
  • Sélecteur : action utilisée pour effectuer le tri. Laissez vide si l’utilisateur ne peut pas trier cette colonne.
  • Ordre : ordre de tri des données des colonnes.
  • Redimensionnement : sélectionne le type de redimensionnement de la colonne.
  • Modifiable : si true, l’utilisateur peut modifier les cellules d’un tableau basé sur des cellules.
  • Masqué : si true, la colonne est masquée.

Vous pouvez également redimensionner la colonne en faisant glisser sa poignée (centrée verticalement sur le côté droit de la colonne) vers la gauche ou la droite.

Nous allons sélectionner chaque colonne dans notre vue Table et attribuer à la première colonne un titre et Product la deuxième Detailscolonne .

Sélectionnez une vue de cellule de tableau (NSTableViewCell) dans la hiérarchie d’interface et les propriétés suivantes sont disponibles dans l’inspecteur d’attributs :

Capture d’écran montrant les propriétés disponibles pour une vue cellule de tableau dans l’inspecteur d’attribut.

Il s’agit de toutes les propriétés d’une vue standard. Vous avez également la possibilité de redimensionner les lignes de cette colonne ici.

Sélectionnez une cellule d’affichage de tableau (par défaut, il s’agit d’un NSTextField) dans la hiérarchie d’interface et les propriétés suivantes sont disponibles dans l’inspecteur d’attributs :

Capture d’écran montrant les propriétés disponibles pour une cellule d’affichage de tableau dans l’inspecteur d’attribut.

Vous aurez toutes les propriétés d’un champ de texte standard à définir ici. Par défaut, un champ de texte standard est utilisé pour afficher les données d’une cellule dans une colonne.

Sélectionnez une vue de cellule de tableau (NSTableFieldCell) dans la hiérarchie d’interface et les propriétés suivantes sont disponibles dans l’inspecteur d’attributs :

Capture d’écran montrant les propriétés disponibles pour une autre cellule d’affichage de tableau dans l’inspecteur d’attributs.

Les paramètres les plus importants sont les suivants :

  • Disposition : sélectionnez la façon dont les cellules de cette colonne sont disposées.
  • Utilise le mode ligne unique : si true, la cellule est limitée à une seule ligne.
  • Largeur de la disposition du premier runtime : si true, la cellule préfère la largeur définie pour elle (manuellement ou automatiquement) lorsqu’elle s’affiche lors de la première exécution de l’application.
  • Action : contrôle l’envoi de l’action Modifier pour la cellule.
  • Comportement : définit si une cellule est sélectionnable ou modifiable.
  • Texte enrichi : si la valeur est true, la cellule peut afficher du texte mis en forme et mis en forme.
  • Annuler : si true, la cellule assume la responsabilité de son comportement d’annulation.

Sélectionnez l’affichage cellule de tableau (NSTableFieldCell) en bas d’une colonne de table dans la hiérarchie d’interface :

Sélection de l’affichage des cellules de tableau

Cela vous permet de modifier la vue cellule de tableau utilisée comme modèle de base pour toutes les cellules créées pour la colonne donnée.

Ajout d’actions et de sorties

Comme tout autre contrôle d’interface utilisateur Cocoa, nous devons exposer notre vue Table et ses colonnes et cellules au code C# à l’aide d’actions et de sorties (en fonction des fonctionnalités requises).

Le processus est le même pour tout élément Table View que nous voulons exposer :

  1. Basculez vers l’Éditeur assistant et vérifiez que le ViewController.h fichier est sélectionné :

    Éditeur assistant

  2. Sélectionnez l’affichage Table dans la hiérarchie d’interface, cliquez avec la touche Ctrl enfoncée et faites glisser vers le ViewController.h fichier.

  3. Créez un outlet pour le mode Table appelé ProductTable:

    Capture d’écran montrant une connexion Outlet créée pour le mode Table nommée ProductTable.

  4. Créez des sorties pour les colonnes de tables ainsi appelées ProductColumn et DetailsColumn:

    Capture d’écran montrant une connexion Outlet créée pour d’autres vues de table.

  5. Enregistrez les modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Ensuite, nous allons écrire le code affichant des données pour la table lors de l’exécution de l’application.

Remplissage de l’affichage Table

Avec notre vue table conçue dans Le Générateur d’interface et exposée via un outlet, nous devons ensuite créer le code C# pour le remplir.

Tout d’abord, nous allons créer une Product classe pour contenir les informations des lignes individuelles. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez Product comme Nom, puis cliquez sur le bouton Nouveau :

Création d’une classe vide

Faites en sorte que le Product.cs fichier ressemble à ce qui suit :

using System;

namespace MacTables
{
  public class Product
  {
    #region Computed Properties
    public string Title { get; set;} = "";
    public string Description { get; set;} = "";
    #endregion

    #region Constructors
    public Product ()
    {
    }

    public Product (string title, string description)
    {
      this.Title = title;
      this.Description = description;
    }
    #endregion
  }
}

Ensuite, nous devons créer une sous-classe de NSTableDataSource pour fournir les données de notre table à mesure qu’elles sont demandées. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez ProductTableDataSource comme Nom, puis cliquez sur le bouton Nouveau.

Modifiez le ProductTableDataSource.cs fichier et faites-le ressembler à ce qui suit :

using System;
using AppKit;
using CoreGraphics;
using Foundation;
using System.Collections;
using System.Collections.Generic;

namespace MacTables
{
  public class ProductTableDataSource : NSTableViewDataSource
  {
    #region Public Variables
    public List<Product> Products = new List<Product>();
    #endregion

    #region Constructors
    public ProductTableDataSource ()
    {
    }
    #endregion

    #region Override Methods
    public override nint GetRowCount (NSTableView tableView)
    {
      return Products.Count;
    }
    #endregion
  }
}

Cette classe dispose d’un stockage pour les éléments de l’affichage table et remplace le GetRowCount pour renvoyer le nombre de lignes dans la table.

Enfin, nous devons créer une sous-classe de NSTableDelegate pour fournir le comportement de notre table. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter un>nouveau fichier... Sélectionnez Classe vide générale>, entrez ProductTableDelegate comme Nom, puis cliquez sur le bouton Nouveau.

Modifiez le ProductTableDelegate.cs fichier et faites-le ressembler à ce qui suit :

using System;
using AppKit;
using CoreGraphics;
using Foundation;
using System.Collections;
using System.Collections.Generic;

namespace MacTables
{
  public class ProductTableDelegate: NSTableViewDelegate
  {
    #region Constants 
    private const string CellIdentifier = "ProdCell";
    #endregion

    #region Private Variables
    private ProductTableDataSource DataSource;
    #endregion

    #region Constructors
    public ProductTableDelegate (ProductTableDataSource datasource)
    {
      this.DataSource = datasource;
    }
    #endregion

    #region Override Methods
    public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
    {
      // This pattern allows you reuse existing views when they are no-longer in use.
      // If the returned view is null, you instance up a new view
      // If a non-null view is returned, you modify it enough to reflect the new data
      NSTextField view = (NSTextField)tableView.MakeView (CellIdentifier, this);
      if (view == null) {
        view = new NSTextField ();
        view.Identifier = CellIdentifier;
        view.BackgroundColor = NSColor.Clear;
        view.Bordered = false;
        view.Selectable = false;
        view.Editable = false;
      }

      // Setup view based on the column selected
      switch (tableColumn.Title) {
      case "Product":
        view.StringValue = DataSource.Products [(int)row].Title;
        break;
      case "Details":
        view.StringValue = DataSource.Products [(int)row].Description;
        break;
      }

      return view;
    }
    #endregion
  }
}

Lorsque nous créons une instance du ProductTableDelegate, nous passons également une instance du ProductTableDataSource qui fournit les données de la table. La GetViewForItem méthode est chargée de renvoyer une vue (données) pour afficher la cellule d’une colonne et d’une ligne de type « give ». Si possible, une vue existante est réutilisée pour afficher la cellule, sinon une nouvelle vue doit être créée.

Pour remplir la table, nous allons modifier le ViewController.cs fichier et faire en sorte que la AwakeFromNib méthode ressemble à ce qui suit :

public override void AwakeFromNib ()
{
  base.AwakeFromNib ();

  // Create the Product Table Data Source and populate it
  var DataSource = new ProductTableDataSource ();
  DataSource.Products.Add (new Product ("Xamarin.iOS", "Allows you to develop native iOS Applications in C#"));
  DataSource.Products.Add (new Product ("Xamarin.Android", "Allows you to develop native Android Applications in C#"));
  DataSource.Products.Add (new Product ("Xamarin.Mac", "Allows you to develop Mac native Applications in C#"));

  // Populate the Product Table
  ProductTable.DataSource = DataSource;
  ProductTable.Delegate = new ProductTableDelegate (DataSource);
}

Si nous exécutons l’application, les éléments suivants s’affichent :

Capture d’écran montrant une fenêtre nommée Table de produits avec trois entrées.

Tri par colonne

Nous allons autoriser l’utilisateur à trier les données de la table en cliquant sur un en-tête de colonne. Tout d’abord, double-cliquez sur le Main.storyboard fichier pour l’ouvrir et le modifier dans Interface Builder. Sélectionnez la Product colonne, entrez Title pour la clé de tri, compare: pour le sélecteur , puis sélectionnez Ascending la commande :

Capture d’écran montrant le Générateur d’interface où vous pouvez définir la clé de tri pour la colonne Product.

Sélectionnez la Details colonne, entrez Description pour la clé de tri, compare: pour le sélecteur , puis sélectionnez Ascending la commande :

Capture d’écran montrant le Générateur d’interface où vous pouvez définir la clé de tri pour la colonne Détails.

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Nous allons maintenant modifier le ProductTableDataSource.cs fichier et ajouter les méthodes suivantes :

public void Sort(string key, bool ascending) {

  // Take action based on key
  switch (key) {
  case "Title":
    if (ascending) {
      Products.Sort ((x, y) => x.Title.CompareTo (y.Title));
    } else {
      Products.Sort ((x, y) => -1 * x.Title.CompareTo (y.Title));
    }
    break;
  case "Description":
    if (ascending) {
      Products.Sort ((x, y) => x.Description.CompareTo (y.Description));
    } else {
      Products.Sort ((x, y) => -1 * x.Description.CompareTo (y.Description));
    }
    break;
  }

}

public override void SortDescriptorsChanged (NSTableView tableView, NSSortDescriptor[] oldDescriptors)
{
  // Sort the data
  if (oldDescriptors.Length > 0) {
    // Update sort
    Sort (oldDescriptors [0].Key, oldDescriptors [0].Ascending);
  } else {
    // Grab current descriptors and update sort
    NSSortDescriptor[] tbSort = tableView.SortDescriptors; 
    Sort (tbSort[0].Key, tbSort[0].Ascending); 
  }
      
  // Refresh table
  tableView.ReloadData ();
}

La Sort méthode nous permet de trier les données dans la source de données en fonction d’un champ de classe donné Product dans l’ordre croissant ou décroissant. La méthode remplacée SortDescriptorsChanged est appelée chaque fois que l’utilisation clique sur un en-tête de colonne. La valeur clé que nous définissons dans Le Générateur d’interface et l’ordre de tri de cette colonne lui seront transmises.

Si nous exécutons l’application et que nous cliquons dans les en-têtes de colonne, les lignes seront triées selon cette colonne :

Exemple d’exécution d’application

Sélection de ligne

Si vous souhaitez autoriser l’utilisateur à sélectionner une seule ligne, double-cliquez sur le fichier pour l’ouvrir Main.storyboard et le modifier dans le Générateur d’interface. Sélectionnez l’affichage Table dans la hiérarchie d’interface et décochez la case à cocher Multiple dans l’inspecteur d’attributs :

Capture d’écran montrant le Générateur d’interface où vous pouvez sélectionner Multiple dans l’inspecteur d’attributs.

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Ensuite, modifiez le ProductTableDelegate.cs fichier et ajoutez la méthode suivante :

public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
  return true;
}

Cela permet à l’utilisateur de sélectionner n’importe quelle ligne dans le mode Tableau. Retournez false le ShouldSelectRow pour toute ligne que vous ne souhaitez pas que l’utilisateur puisse sélectionner ou false pour chaque ligne si vous ne souhaitez pas que l’utilisateur puisse sélectionner des lignes.

Le mode Table (NSTableView) contient les méthodes suivantes pour l’utilisation de la sélection de lignes :

  • DeselectRow(nint) - Désélectionne la ligne donnée dans le tableau.
  • SelectRow(nint,bool) - Sélectionne la ligne donnée. false Passez le deuxième paramètre pour ne sélectionner qu’une seule ligne à la fois.
  • SelectedRow - Retourne la ligne actuelle sélectionnée dans le tableau.
  • IsRowSelected(nint) - Retourne true si la ligne donnée est sélectionnée.

Sélection de plusieurs lignes

Si vous souhaitez autoriser l’utilisateur à sélectionner plusieurs lignes, double-cliquez sur le fichier pour l’ouvrir Main.storyboard et le modifier dans le Générateur d’interface. Sélectionnez la vue Table dans la hiérarchie d’interface et case activée la case à cocher Multiple dans l’inspecteur d’attributs :

Capture d’écran montrant le Générateur d’interface où vous pouvez sélectionner Plusieurs pour autoriser la sélection de plusieurs lignes.

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Ensuite, modifiez le ProductTableDelegate.cs fichier et ajoutez la méthode suivante :

public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
  return true;
}

Cela permet à l’utilisateur de sélectionner n’importe quelle ligne dans le mode Tableau. Retournez false le ShouldSelectRow pour toute ligne que vous ne souhaitez pas que l’utilisateur puisse sélectionner ou false pour chaque ligne si vous ne souhaitez pas que l’utilisateur puisse sélectionner des lignes.

Le mode Table (NSTableView) contient les méthodes suivantes pour l’utilisation de la sélection de lignes :

  • DeselectAll(NSObject) - Désélectionne toutes les lignes de la table. Utilisez this pour le premier paramètre à envoyer l’objet en effectuant la sélection.
  • DeselectRow(nint) - Désélectionne la ligne donnée dans le tableau.
  • SelectAll(NSobject) - Sélectionne toutes les lignes de la table. Utilisez this pour le premier paramètre à envoyer l’objet en effectuant la sélection.
  • SelectRow(nint,bool) - Sélectionne la ligne donnée. false Passez pour le deuxième paramètre effacez la sélection et sélectionnez une seule ligne, passez true pour étendre la sélection et inclure cette ligne.
  • SelectRows(NSIndexSet,bool) - Sélectionne l’ensemble de lignes donné. false Passez pour le deuxième paramètre effacez la sélection et sélectionnez uniquement ces lignes, passez true pour étendre la sélection et inclure ces lignes.
  • SelectedRow - Retourne la ligne actuelle sélectionnée dans le tableau.
  • SelectedRows - Retourne un NSIndexSet contenant les index des lignes sélectionnées.
  • SelectedRowCount - Retourne le nombre de lignes sélectionnées.
  • IsRowSelected(nint) - Retourne true si la ligne donnée est sélectionnée.

Tapez pour sélectionner la ligne

Si vous souhaitez autoriser l’utilisateur à taper un caractère avec le mode Tableau sélectionné et à sélectionner la première ligne contenant ce caractère, double-cliquez sur le fichier pour l’ouvrir Main.storyboard et le modifier dans le Générateur d’interface. Sélectionnez l’affichage Table dans la hiérarchie d’interface et case activée la case à cocher Sélectionner le type dans l’inspecteur d’attributs :

Définition du type de sélection

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Nous allons maintenant modifier le ProductTableDelegate.cs fichier et ajouter la méthode suivante :

public override nint GetNextTypeSelectMatch (NSTableView tableView, nint startRow, nint endRow, string searchString)
{
  nint row = 0;
  foreach(Product product in DataSource.Products) {
    if (product.Title.Contains(searchString)) return row;

    // Increment row counter
    ++row;
  }

  // If not found select the first row
  return 0;
}

La GetNextTypeSelectMatch méthode prend le donné searchString et retourne la ligne du premier Product qui contient cette chaîne dans .Title

Si nous exécutons l’application et tapez un caractère, une ligne est sélectionnée :

Capture d’écran montrant le résultat de l’exécution de l’application.

Réorganisation des colonnes

Si vous souhaitez autoriser l’utilisateur à faire glisser des colonnes de réorganisation dans le mode Tableau, double-cliquez sur le fichier pour l’ouvrir Main.storyboard et le modifier dans le Générateur d’interface. Sélectionnez la vue Table dans la hiérarchie d’interface et case activée la case à cocher Réorganiser dans l’inspecteur d’attribut :

Capture d’écran montrant le Générateur d’interface où vous pouvez sélectionner Reodering dans l’Inspecteur d’attribut.

Si nous donnons une valeur pour la propriété Enregistrement automatique et case activée le champ Informations sur la colonne, toutes les modifications que nous apporterons à la disposition de la table seront automatiquement enregistrées pour nous et restaurées lors de la prochaine exécution de l’application.

Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Nous allons maintenant modifier le ProductTableDelegate.cs fichier et ajouter la méthode suivante :

public override bool ShouldReorder (NSTableView tableView, nint columnIndex, nint newColumnIndex)
{
  return true;
}

La ShouldReorder méthode doit retourner true pour toute colonne qu’elle souhaite autoriser à faire glisser la réorganisation dans le newColumnIndex, sinon, retourne false;

Si nous exécutons l’application, nous pouvons faire glisser les en-têtes de colonne pour réorganiser nos colonnes :

Exemple de colonnes réorganisées

Modification des cellules

Si vous souhaitez autoriser l’utilisateur à modifier les valeurs d’une cellule donnée, modifiez le ProductTableDelegate.cs fichier et modifiez la GetViewForItem méthode comme suit :

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTextField view = (NSTextField)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTextField ();
    view.Identifier = tableColumn.Title;
    view.BackgroundColor = NSColor.Clear;
    view.Bordered = false;
    view.Selectable = false;
    view.Editable = true;

    view.EditingEnded += (sender, e) => {
          
      // Take action based on type
      switch(view.Identifier) {
      case "Product":
        DataSource.Products [(int)view.Tag].Title = view.StringValue;
        break;
      case "Details":
        DataSource.Products [(int)view.Tag].Description = view.StringValue;
        break; 
      }
    };
  }

  // Tag view
  view.Tag = row;

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.StringValue = DataSource.Products [(int)row].Title;
    break;
  case "Details":
    view.StringValue = DataSource.Products [(int)row].Description;
    break;
  }

  return view;
}

Maintenant, si nous exécutons l’application, l’utilisateur peut modifier les cellules dans l’affichage Tableau :

Exemple de modification d’une cellule

Utilisation d’images dans les vues de tableau

Pour inclure une image dans la cellule d’un NSTableView, vous devez modifier la façon dont les données sont retournées par la méthode deGetViewForItemNSTableViewDelegate'sl’affichage Table afin d’utiliser un NSTableCellView au lieu du type standard NSTextField. Par exemple :

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{

  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTableCellView ();
    if (tableColumn.Title == "Product") {
      view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
      view.AddSubview (view.ImageView);
      view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
    } else {
      view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
    }
    view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
    view.AddSubview (view.TextField);
    view.Identifier = tableColumn.Title;
    view.TextField.BackgroundColor = NSColor.Clear;
    view.TextField.Bordered = false;
    view.TextField.Selectable = false;
    view.TextField.Editable = true;

    view.TextField.EditingEnded += (sender, e) => {

      // Take action based on type
      switch(view.Identifier) {
      case "Product":
        DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
        break;
      case "Details":
        DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
        break; 
      }
    };
  }

  // Tag view
  view.TextField.Tag = row;

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.ImageView.Image = NSImage.ImageNamed ("tags.png");
    view.TextField.StringValue = DataSource.Products [(int)row].Title;
    break;
  case "Details":
    view.TextField.StringValue = DataSource.Products [(int)row].Description;
    break;
  }

  return view;
}

Pour plus d’informations, consultez la section Utilisation d’images avec des vues de tableau de notre documentation Utilisation des images .

Ajout d’un bouton Supprimer à une ligne

En fonction des exigences de votre application, il peut arriver que vous deviez fournir un bouton d’action pour chaque ligne du tableau. À titre d’exemple, nous allons développer l’exemple d’affichage table créé ci-dessus pour inclure un bouton Supprimer sur chaque ligne.

Tout d’abord, modifiez le Main.storyboard dans le Générateur d’interface de Xcode, sélectionnez l’affichage Table et augmentez le nombre de colonnes à trois (3). Ensuite, remplacez le titre de la nouvelle colonne par Action:

Modification du nom de colonne

Enregistrez les modifications apportées au Storyboard et revenez à Visual Studio pour Mac pour synchroniser les modifications.

Ensuite, modifiez le ViewController.cs fichier et ajoutez la méthode publique suivante :

public void ReloadTable ()
{
  ProductTable.ReloadData ();
}

Dans le même fichier, modifiez la création du délégué d’affichage table à l’intérieur de la ViewDidLoad méthode comme suit :

// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (this, DataSource);

À présent, modifiez le ProductTableDelegate.cs fichier pour inclure une connexion privée au contrôleur d’affichage et prendre le contrôleur en tant que paramètre lors de la création d’une nouvelle instance du délégué :

#region Private Variables
private ProductTableDataSource DataSource;
private ViewController Controller;
#endregion

#region Constructors
public ProductTableDelegate (ViewController controller, ProductTableDataSource datasource)
{
  this.Controller = controller;
  this.DataSource = datasource;
}
#endregion

Ensuite, ajoutez la nouvelle méthode privée suivante à la classe :

private void ConfigureTextField (NSTableCellView view, nint row)
{
  // Add to view
  view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
  view.AddSubview (view.TextField);

  // Configure
  view.TextField.BackgroundColor = NSColor.Clear;
  view.TextField.Bordered = false;
  view.TextField.Selectable = false;
  view.TextField.Editable = true;

  // Wireup events
  view.TextField.EditingEnded += (sender, e) => {

    // Take action based on type
    switch (view.Identifier) {
    case "Product":
      DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
      break;
    case "Details":
      DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
      break;
    }
  };

  // Tag view
  view.TextField.Tag = row;
}

Cette opération prend toutes les configurations de l’affichage texte qui ont été effectuées précédemment dans la GetViewForItem méthode et les place dans un emplacement unique pouvant être appelé (puisque la dernière colonne du tableau n’inclut pas un affichage texte, mais un bouton).

Enfin, modifiez la GetViewForItem méthode et faites-la ressembler à ce qui suit :

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{

  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTableCellView ();

    // Configure the view
    view.Identifier = tableColumn.Title;

    // Take action based on title
    switch (tableColumn.Title) {
    case "Product":
      view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
      view.AddSubview (view.ImageView);
      view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
      ConfigureTextField (view, row);
      break;
    case "Details":
      view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
      ConfigureTextField (view, row);
      break;
    case "Action":
      // Create new button
      var button = new NSButton (new CGRect (0, 0, 81, 16));
      button.SetButtonType (NSButtonType.MomentaryPushIn);
      button.Title = "Delete";
      button.Tag = row;

      // Wireup events
      button.Activated += (sender, e) => {
        // Get button and product
        var btn = sender as NSButton;
        var product = DataSource.Products [(int)btn.Tag];

        // Configure alert
        var alert = new NSAlert () {
          AlertStyle = NSAlertStyle.Informational,
          InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
          MessageText = $"Delete {product.Title}?",
        };
        alert.AddButton ("Cancel");
        alert.AddButton ("Delete");
        alert.BeginSheetForResponse (Controller.View.Window, (result) => {
          // Should we delete the requested row?
          if (result == 1001) {
            // Remove the given row from the dataset
            DataSource.Products.RemoveAt((int)btn.Tag);
            Controller.ReloadTable ();
          }
        });
      };

      // Add to view
      view.AddSubview (button);
      break;
    }

  }

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.ImageView.Image = NSImage.ImageNamed ("tag.png");
    view.TextField.StringValue = DataSource.Products [(int)row].Title;
    view.TextField.Tag = row;
    break;
  case "Details":
    view.TextField.StringValue = DataSource.Products [(int)row].Description;
    view.TextField.Tag = row;
    break;
  case "Action":
    foreach (NSView subview in view.Subviews) {
      var btn = subview as NSButton;
      if (btn != null) {
        btn.Tag = row;
      }
    }
    break;
  }

  return view;
}

Examinons plusieurs sections de ce code plus en détail. Tout d’abord, si une NSTableViewCell action est en cours de création est effectuée en fonction du nom de la colonne. Pour les deux premières colonnes (Product et Details), la nouvelle ConfigureTextField méthode est appelée.

Pour la colonne Action , un nouveau NSButton est créé et ajouté à la cellule en tant que sous-affichage :

// Create new button
var button = new NSButton (new CGRect (0, 0, 81, 16));
button.SetButtonType (NSButtonType.MomentaryPushIn);
button.Title = "Delete";
button.Tag = row;
...

// Add to view
view.AddSubview (button);

La propriété du Tag bouton est utilisée pour contenir le numéro de la ligne en cours de traitement. Ce numéro sera utilisé ultérieurement lorsque l’utilisateur demande la suppression d’une ligne dans l’événement button Activated :

// Wireup events
button.Activated += (sender, e) => {
  // Get button and product
  var btn = sender as NSButton;
  var product = DataSource.Products [(int)btn.Tag];

  // Configure alert
  var alert = new NSAlert () {
    AlertStyle = NSAlertStyle.Informational,
    InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
    MessageText = $"Delete {product.Title}?",
  };
  alert.AddButton ("Cancel");
  alert.AddButton ("Delete");
  alert.BeginSheetForResponse (Controller.View.Window, (result) => {
    // Should we delete the requested row?
    if (result == 1001) {
      // Remove the given row from the dataset
      DataSource.Products.RemoveAt((int)btn.Tag);
      Controller.ReloadTable ();
    }
  });
};

Au début du gestionnaire d’événements, nous obtenons le bouton et le produit qui se trouve sur la ligne de table donnée. Ensuite, une alerte est présentée à l’utilisateur confirmant la suppression de la ligne. Si l’utilisateur choisit de supprimer la ligne, la ligne donnée est supprimée de la source de données et la table est rechargée :

// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();

Enfin, si la cellule Vue de tableau est réutilisée au lieu d’être créée, le code suivant la configure en fonction de la colonne en cours de traitement :

// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
  view.ImageView.Image = NSImage.ImageNamed ("tag.png");
  view.TextField.StringValue = DataSource.Products [(int)row].Title;
  view.TextField.Tag = row;
  break;
case "Details":
  view.TextField.StringValue = DataSource.Products [(int)row].Description;
  view.TextField.Tag = row;
  break;
case "Action":
  foreach (NSView subview in view.Subviews) {
    var btn = subview as NSButton;
    if (btn != null) {
      btn.Tag = row;
    }
  }
  break;
}

Pour la colonne Action , tous les sous-affichages sont analysés jusqu’à ce que le NSButton soit trouvé, puis sa Tag propriété est mise à jour pour pointer vers la ligne actuelle.

Une fois ces modifications en place, lorsque l’application est exécutée, chaque ligne dispose d’un bouton Supprimer :

Vue table avec des boutons de suppression

Lorsque l’utilisateur clique sur un bouton Supprimer , une alerte s’affiche lui demandant de supprimer la ligne donnée :

Une alerte de suppression de ligne

Si l’utilisateur choisit supprimer, la ligne est supprimée et la table est redessinée :

Table après la suppression de la ligne

Vues de table de liaison de données

En utilisant Key-Value techniques de codage et de liaison de données dans votre application Xamarin.Mac, vous pouvez réduire considérablement la quantité de code que vous devez écrire et gérer pour remplir et utiliser des éléments d’interface utilisateur. Vous avez également l’avantage de découpler davantage vos données de stockage (modèle de données) de votre interface utilisateur frontale (Model-View-Controller), ce qui permet une conception d’application plus facile à gérer et plus flexible.

Key-Value Coding (KVC) est un mécanisme permettant d’accéder indirectement aux propriétés d’un objet, en utilisant des clés (chaînes spécialement mises en forme) pour identifier les propriétés au lieu d’y accéder via des variables instance ou des méthodes d’accesseur (get/set). En implémentant des accesseurs compatibles codage Key-Value dans votre application Xamarin.Mac, vous obtenez l’accès à d’autres fonctionnalités macOS telles que l’observation Key-Value (KVO), la liaison de données, les données principales, les liaisons Cocoa et la scriptabilité.

Pour plus d’informations, consultez la section Liaison de données d’affichage table de notre documentation sur la liaison de données et le codage Key-Value .

Résumé

Cet article a examiné en détail l’utilisation des vues de tableau dans une application Xamarin.Mac. Nous avons vu les différents types et utilisations des vues de table, comment créer et gérer des vues de table dans le générateur d’interface de Xcode et comment utiliser des vues de table en code C#.