Viste tabella in Xamarin.Mac

Questo articolo illustra l'uso delle viste tabella in un'applicazione Xamarin.Mac. Descrive la creazione di viste tabella in Xcode e Interface Builder e l'interazione con esse nel codice.

Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere alle stesse viste tabella in cui uno sviluppatore lavora Objective-C e Xcode . Poiché Xamarin.Mac si integra direttamente con Xcode, è possibile usare Interface Builder di Xcode per creare e gestire le viste tabella (o, facoltativamente, crearle direttamente nel codice C#).

Una vista tabella visualizza i dati in un formato tabulare contenente una o più colonne di informazioni in più righe. In base al tipo di vista tabella in fase di creazione, l'utente può ordinare in base alla colonna, riorganizzare le colonne, aggiungere colonne, rimuovere colonne o modificare i dati contenuti nella tabella.

An example table

In questo articolo verranno illustrate le nozioni di base sull'uso delle viste tabella in un'applicazione Xamarin.Mac. È consigliabile usare prima di tutto l'articolo Hello, Mac , in particolare le sezioni Introduzione a Xcode e Interface Builder e Outlet e Actions , in quanto illustra i concetti e le tecniche chiave che verranno usati in questo articolo.

È possibile esaminare anche la sezione Esposizione di classi/metodi C# alObjective-Cdocumento Internals di Xamarin.Mac, che illustra anche i Register comandi e Export usati per collegare le classi C# agli oggetti e agli Objective-C elementi dell'interfaccia utente.

Introduzione alle viste tabella

Una vista tabella visualizza i dati in un formato tabulare contenente una o più colonne di informazioni in più righe. Le visualizzazioni tabella vengono visualizzate all'interno di Visualizzazioni di scorrimento (NSScrollView) e a partire da macOS 10.7, è possibile usare qualsiasi NSView anziché celle (NSCell) per visualizzare sia le righe che le colonne. Detto questo, è comunque possibile usare NSCell , in genere si creeranno NSTableCellView righe e colonne personalizzate.

Una vista tabella non archivia i propri dati, ma si basa su un'origine dati (NSTableViewDataSource) per fornire sia le righe che le colonne necessarie, in base alle esigenze.

Il comportamento di una vista tabella può essere personalizzato fornendo una sottoclasse del delegato di visualizzazione tabella (NSTableViewDelegate) per supportare la gestione delle colonne della tabella, digitare per selezionare funzionalità, selezione di righe e modifica, rilevamento personalizzato e visualizzazioni personalizzate per singole colonne e righe.

Quando si creano viste tabella, Apple suggerisce quanto segue:

  • Consentire all'utente di ordinare la tabella facendo clic su intestazioni di colonna.
  • Creare intestazioni di colonna che sono sostantivi o brevi frasi sostantive che descrivono i dati visualizzati in tale colonna.

Per altre informazioni, vedere la sezione Visualizzazioni contenuto delle linee guida sull'interfaccia umana di OS X di Apple.

Creazione e gestione di viste tabella in Xcode

Quando si crea una nuova applicazione Xamarin.Mac Cocoa, si ottiene una finestra vuota standard per impostazione predefinita. Questa finestra viene definita in un .storyboard file incluso automaticamente nel progetto. Per modificare la progettazione delle finestre, nella Esplora soluzioni fare doppio clic sul Main.storyboard file:

Selecting the main storyboard

Verrà aperta la progettazione della finestra in Interface Builder di Xcode:

Editing the UI in Xcode

Digitare table nella casella di ricerca di Controllo libreria per semplificare la ricerca dei controlli Visualizzazione tabella:

Selecting a Table View from the Library

Trascinare una visualizzazione tabella nel controller di visualizzazione nell'editor dell'interfaccia, riempire l'area del contenuto del controller di visualizzazione e impostarla su dove si riduce e aumenta con la finestra nell'editor dei vincoli:

Editing constraints

Selezionare la visualizzazione tabella nella gerarchia dell'interfaccia e le proprietà seguenti sono disponibili in Controllo attributi:

Screenshot shows the properties available in the Attribute Inspector.

  • Modalità contenuto: consente di usare visualizzazioni () o celle (NSViewNSCell) per visualizzare i dati nelle righe e nelle colonne. A partire da macOS 10.7, è consigliabile usare Views.
  • Floats Group Rows : se true, la vista tabella disegnare celle raggruppate come se fossero mobili.
  • Colonne : definisce il numero di colonne visualizzate.
  • Intestazioni : se true, le colonne avranno intestazioni.
  • Riordinamento : se true, l'utente sarà in grado di trascinare riordinare le colonne nella tabella.
  • Ridimensionamento : se true, l'utente sarà in grado di trascinare le intestazioni di colonna per ridimensionare le colonne.
  • Ridimensionamento delle colonne: controlla la modalità di ridimensionamento automatico delle colonne della tabella.
  • Evidenziazione : controlla il tipo di evidenziazione della tabella utilizzata quando viene selezionata una cella.
  • Righe alternative: se true, in qualsiasi altra riga sarà presente un colore di sfondo diverso.
  • Griglia orizzontale: seleziona il tipo di bordo disegnato tra le celle orizzontalmente.
  • Griglia verticale: seleziona il tipo di bordo disegnato tra le celle verticalmente.
  • Colore griglia: imposta il colore del bordo della cella.
  • Sfondo : imposta il colore di sfondo della cella.
  • Selezione : consente di controllare come l'utente può selezionare le celle nella tabella come:
    • Multiple : se true, l'utente può selezionare più righe e colonne.
    • Colonna: se true, l'utente può selezionare le colonne.
    • Digitare Select : se true, l'utente può digitare un carattere per selezionare una riga.
    • Vuoto : se true, l'utente non è necessario selezionare una riga o una colonna, la tabella non consente alcuna selezione.
  • Salvataggio automatico: nome in cui viene salvato automaticamente il formato delle tabelle.
  • Informazioni sulla colonna: se true, l'ordine e la larghezza delle colonne verranno salvati automaticamente.
  • Interruzioni di riga: consente di selezionare la modalità di gestione delle interruzioni di riga.
  • Tronca l'ultima riga visibile: se true, la cella verrà troncata nei dati non può essere inserita all'interno dei limiti.

Importante

A meno che non si gestisca un'applicazione Xamarin.Mac legacy, NSView è consigliabile usare viste tabella basate su NSCell viste tabella basate. NSCell è considerato legacy e potrebbe non essere supportato in futuro.

Selezionare una colonna di tabella nella gerarchia dell'interfaccia e le proprietà seguenti sono disponibili in Controllo attributi:

Screenshot shows the properties available for a Table Column in the Attribute Inspector.

  • Titolo : imposta il titolo della colonna.
  • Allineamento : consente di impostare l'allineamento del testo all'interno delle celle.
  • Titolo Carattere : seleziona il tipo di carattere per il testo Intestazione della cella.
  • Chiave di ordinamento: chiave usata per ordinare i dati nella colonna. Lasciare vuoto se l'utente non è in grado di ordinare questa colonna.
  • Selettore: azione usata per eseguire l'ordinamento. Lasciare vuoto se l'utente non è in grado di ordinare questa colonna.
  • Order : ordinamento per i dati delle colonne.
  • Ridimensionamento : seleziona il tipo di ridimensionamento per la colonna.
  • Modificabile : se true, l'utente può modificare le celle in una tabella basata su cella.
  • Nascosto : se true, la colonna è nascosta.

È anche possibile ridimensionare la colonna trascinando il punto di manipolazione (centrato verticalmente sul lato destro della colonna) a sinistra o a destra.

Selezionare ogni colonna nella vista tabella e assegnare alla prima colonna un titolo e Product il secondo Details.

Selezionare una visualizzazione cella tabella (NSTableViewCell) nella gerarchia dell'interfaccia e le proprietà seguenti sono disponibili in Controllo attributi:

Screenshot shows the properties available for a Table Cell View in the Attribute Inspector.

Queste sono tutte le proprietà di una visualizzazione standard. È anche possibile ridimensionare le righe per questa colonna qui.

Selezionare una cella di visualizzazione tabella (per impostazione predefinita, si tratta di ) NSTextFieldnella gerarchia di interfaccia e le proprietà seguenti sono disponibili in Controllo attributi:

Screenshot shows the properties available for a Table View Cell in the Attribute Inspector.

Tutte le proprietà di un campo di testo standard verranno impostate qui. Per impostazione predefinita, viene usato un campo di testo standard per visualizzare i dati di una cella in una colonna.

Selezionare una visualizzazione cella tabella (NSTableFieldCell) nella gerarchia dell'interfaccia e le proprietà seguenti sono disponibili in Controllo attributi:

Screenshot shows the properties available for a different Table View Cell in the Attribute Inspector.

Di seguito sono riportate le impostazioni più importanti:

  • Layout : consente di selezionare la disposizione delle celle in questa colonna.
  • Usa la modalità riga singola: se true, la cella è limitata a una singola riga.
  • Prima larghezza layout runtime: se true, la cella preferisce il set di larghezza (manualmente o automaticamente) quando viene visualizzata la prima volta che viene eseguita l'applicazione.
  • Azione: controlla quando viene inviata l'azione di modifica per la cella.
  • Comportamento : definisce se una cella è selezionabile o modificabile.
  • Rtf: se true, la cella può visualizzare testo formattato e in stile.
  • Annulla: se true, la cella assume la responsabilità del comportamento di annullamento.

Selezionare la vista cella tabella (NSTableFieldCell) nella parte inferiore di una colonna tabella nella gerarchia dell'interfaccia:

Selecting the Table Cell View

In questo modo è possibile modificare la visualizzazione cella tabella usata come modello di base per tutte le celle create per la colonna specificata.

Aggiunta di azioni e punti di vendita

Proprio come qualsiasi altro controllo dell'interfaccia utente cocoa, è necessario esporre la vista tabella e le colonne e le celle al codice C# usando Azioni e outlet (in base alle funzionalità necessarie).

Il processo è lo stesso per qualsiasi elemento Table View che si vuole esporre:

  1. Passare all'Editor assistente e verificare che il ViewController.h file sia selezionato:

    The Assistant Editor

  2. Selezionare la visualizzazione tabella dalla gerarchia dell'interfaccia, fare clic e trascinare nel ViewController.h file.

  3. Creare un outlet per la vista tabella denominata ProductTable:

    Screenshot shows an Outlet connection created for the Table View named ProductTable.

  4. Creare outlet per le colonne delle tabelle denominate ProductColumn e DetailsColumn:

    Screenshot shows an Outlet connections created for other Table Views.

  5. Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

Successivamente, il codice visualizzerà alcuni dati per la tabella quando viene eseguita l'applicazione.

Popolamento della vista tabella

Con la vista tabella progettata in Interface Builder ed esposta tramite un outlet, è quindi necessario creare il codice C# per popolarlo.

Creare prima di tutto una nuova Product classe per contenere le informazioni per le singole righe. Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo file... Selezionare General Empty Class (Classe vuota generale>), immettere Product per Name (Nome) e fare clic sul pulsante New (Nuovo):

Creating an empty class

Fare in modo che il Product.cs file sia simile al seguente:

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

Successivamente, è necessario creare una sottoclasse di NSTableDataSource per fornire i dati per la tabella in quanto richiesto. Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo file... Selezionare General Empty Class (Classe vuota Generale>), immettere ProductTableDataSource per Name (Nome) e fare clic sul pulsante New (Nuovo).

Modificare il ProductTableDataSource.cs file e renderlo simile al seguente:

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

Questa classe dispone di spazio di archiviazione per gli elementi della vista tabella ed esegue l'override GetRowCount di per restituire il numero di righe nella tabella.

Infine, è necessario creare una sottoclasse di NSTableDelegate per fornire il comportamento per la tabella. Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>nuovo file... Selezionare General Empty Class (Classe vuota Generale>), immettere ProductTableDelegate per Name (Nome) e fare clic sul pulsante New (Nuovo).

Modificare il ProductTableDelegate.cs file e renderlo simile al seguente:

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

Quando si crea un'istanza ProductTableDelegatedi , viene passata anche un'istanza ProductTableDataSource di che fornisce i dati per la tabella. Il GetViewForItem metodo è responsabile della restituzione di una vista (dati) per visualizzare la cella per una colonna e una riga di tipo give. Se possibile, verrà riutilizzata una visualizzazione esistente per visualizzare la cella, se non è necessario creare una nuova visualizzazione.

Per popolare la tabella, modificare il ViewController.cs file e fare in modo che il AwakeFromNib metodo sia simile al seguente:

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

Se si esegue l'applicazione, viene visualizzato quanto segue:

Screenshot shows a window named Product Table with three entries.

Ordinamento per colonna

Consentire all'utente di ordinare i dati nella tabella facendo clic su un'intestazione di colonna. Prima di tutto, fare doppio clic sul Main.storyboard file per aprirlo per la modifica in Interface Builder. Selezionare la Product colonna, immettere Title per La chiave di ordinamento, compare: per il selettore e selezionare Ascending per l'ordine:

Screenshot shows the Interface Builder where you can set the sort key for the Product column.

Selezionare la Details colonna, immettere Description per La chiave di ordinamento, compare: per il selettore e selezionare Ascending per l'ordine:

Screenshot shows the Interface Builder where you can set the sort key for the Details column.

Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

A questo punto, modificare il ProductTableDataSource.cs file e aggiungere i metodi seguenti:

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

Il Sort metodo consente di ordinare i dati nell'origine dati in base a un determinato Product campo di classe in ordine crescente o decrescente. Il metodo sottoposto SortDescriptorsChanged a override verrà chiamato ogni volta che l'utilizzo fa clic su un'intestazione di colonna. Verrà passato il valore chiave impostato in Interface Builder e l'ordinamento per tale colonna.

Se si esegue l'applicazione e si fa clic nelle intestazioni di colonna, le righe verranno ordinate in base a tale colonna:

An example app run

Selezione riga

Se si desidera consentire all'utente di selezionare una singola riga, fare doppio clic sul Main.storyboard file per aprirlo per la modifica in Interface Builder. Selezionare la visualizzazione tabella nella gerarchia dell'interfaccia e deselezionare la casella di controllo Multiple (Controllo attributi):

Screenshot shows the Interface Builder where you can select Multiple in the Attribute Inspector.

Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

Modificare quindi il ProductTableDelegate.cs file e aggiungere il metodo seguente:

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

In questo modo l'utente potrà selezionare qualsiasi singola riga nella visualizzazione tabella. Restituisce false per qualsiasi ShouldSelectRow riga che non si vuole che l'utente sia in grado di selezionare o false per ogni riga se non si vuole che l'utente sia in grado di selezionare alcuna riga.

La vista tabella (NSTableView) contiene i metodi seguenti per l'utilizzo della selezione di righe:

  • DeselectRow(nint) - Deseleziona la riga specificata nella tabella.
  • SelectRow(nint,bool) - Seleziona la riga specificata. Passare false per il secondo parametro per selezionare una sola riga alla volta.
  • SelectedRow : restituisce la riga corrente selezionata nella tabella.
  • IsRowSelected(nint) - Restituisce true se la riga specificata è selezionata.

Selezione di più righe

Se si desidera consentire all'utente di selezionare più righe, fare doppio clic sul Main.storyboard file per aprirlo per la modifica in Interface Builder. Selezionare la visualizzazione tabella nella gerarchia dell'interfaccia e selezionare la casella di controllo Multipla in Controllo attributi:

Screenshot shows the Interface Builder where you can select Multiple to allow multiple row selection.

Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

Modificare quindi il ProductTableDelegate.cs file e aggiungere il metodo seguente:

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

In questo modo l'utente potrà selezionare qualsiasi singola riga nella visualizzazione tabella. Restituisce false per qualsiasi ShouldSelectRow riga che non si vuole che l'utente sia in grado di selezionare o false per ogni riga se non si vuole che l'utente sia in grado di selezionare alcuna riga.

La vista tabella (NSTableView) contiene i metodi seguenti per l'utilizzo della selezione di righe:

  • DeselectAll(NSObject) - Deseleziona tutte le righe nella tabella. Utilizzare this per il primo parametro da inviare nell'oggetto eseguendo la selezione.
  • DeselectRow(nint) - Deseleziona la riga specificata nella tabella.
  • SelectAll(NSobject) - Seleziona tutte le righe nella tabella. Utilizzare this per il primo parametro da inviare nell'oggetto eseguendo la selezione.
  • SelectRow(nint,bool) - Seleziona la riga specificata. Passare false per il secondo parametro deselezionare la selezione e selezionare solo una singola riga, passare true per estendere la selezione e includere questa riga.
  • SelectRows(NSIndexSet,bool) - Seleziona il set specificato di righe. Passare false per il secondo parametro deselezionare la selezione e selezionare solo una di queste righe, passare true per estendere la selezione e includere queste righe.
  • SelectedRow : restituisce la riga corrente selezionata nella tabella.
  • SelectedRows : restituisce un oggetto NSIndexSet contenente gli indici delle righe selezionate.
  • SelectedRowCount : restituisce il numero di righe selezionate.
  • IsRowSelected(nint) - Restituisce true se la riga specificata è selezionata.

Digitare per selezionare riga

Se si desidera consentire all'utente di digitare un carattere con la visualizzazione tabella selezionata e selezionare la prima riga con tale carattere, fare doppio clic sul Main.storyboard file per aprirlo per la modifica in Interface Builder. Selezionare la visualizzazione tabella nella gerarchia dell'interfaccia e selezionare la casella di controllo Type Select (Seleziona tipo) in Controllo attributi:

Setting the selection type

Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

A questo punto, modificare il ProductTableDelegate.cs file e aggiungere il metodo seguente:

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

Il GetNextTypeSelectMatch metodo accetta l'oggetto specificato searchString e restituisce la riga della prima Product che contiene tale stringa in .Title

Se si esegue l'applicazione e si digita un carattere, viene selezionata una riga:

Screenshot shows the result of running the application.

Riordinare le colonne

Se si desidera consentire all'utente di trascinare le colonne di riordinamento nella visualizzazione tabella, fare doppio clic sul Main.storyboard file per aprirlo per la modifica in Interface Builder. Selezionare la visualizzazione tabella nella gerarchia dell'interfaccia e selezionare la casella di controllo Riordinamento in Controllo attributi:

Screenshot shows the Interface Builder where you can select Reodering in the Attribute Inspector.

Se si assegna un valore per la proprietà Salvataggio automatico e si controlla il campo Informazioni colonna, le modifiche apportate al layout della tabella verranno salvate automaticamente e ripristinate al successivo esecuzione dell'applicazione.

Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

A questo punto, modificare il ProductTableDelegate.cs file e aggiungere il metodo seguente:

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

Il ShouldReorder metodo deve restituire true per qualsiasi colonna che desidera consentire il trascinamento del trascinamento nell'oggetto newColumnIndex, altrimenti restituire false;

Se si esegue l'applicazione, è possibile trascinare le intestazioni di colonna per riordinare le colonne:

An example of the reordered columns

Modifica di celle

Se si desidera consentire all'utente di modificare i valori per una determinata cella, modificare il ProductTableDelegate.cs file e modificare il GetViewForItem metodo come indicato di seguito:

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

A questo punto, se si esegue l'applicazione, l'utente può modificare le celle nella visualizzazione Tabella:

An example of editing a cell

Uso di immagini nelle viste tabella

Per includere un'immagine come parte della cella in un NSTableViewoggetto , è necessario modificare il modo in cui i dati vengono restituiti dal metodo di NSTableViewDelegate'sGetViewForItem Visualizzazione tabella per usare un NSTableCellView anziché il tipico NSTextField. Ad esempio:

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

Per altre informazioni, vedere la sezione Uso di immagini con viste tabella della documentazione sull'uso dell'immagine.

Aggiunta di un pulsante Elimina a una riga

In base ai requisiti dell'app, potrebbero esserci occasioni in cui è necessario specificare un pulsante di azione per ogni riga della tabella. Di seguito è riportato un esempio di visualizzazione tabella creato in precedenza per includere un pulsante Elimina in ogni riga.

Prima di tutto, modificare in Main.storyboard Interface Builder di Xcode, selezionare la visualizzazione tabella e aumentare il numero di colonne a tre (3). Modificare quindi il titolo della nuova colonna in Action:

Editing the column name

Salvare le modifiche apportate allo Storyboard e tornare a Visual Studio per Mac per sincronizzare le modifiche.

Modificare quindi il ViewController.cs file e aggiungere il metodo pubblico seguente:

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

Nello stesso file modificare la creazione del nuovo delegato di visualizzazione tabella all'interno del ViewDidLoad metodo come indicato di seguito:

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

Modificare ora il ProductTableDelegate.cs file in modo da includere una connessione privata al controller di visualizzazione e accettare il controller come parametro durante la creazione di una nuova istanza del delegato:

#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

Aggiungere quindi il nuovo metodo privato seguente alla 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;
}

Questa operazione accetta tutte le configurazioni della visualizzazione testo eseguite in precedenza nel GetViewForItem metodo e le inserisce in un'unica posizione chiamabile (poiché l'ultima colonna della tabella non include una visualizzazione testo ma un pulsante).

Infine, modificare il GetViewForItem metodo e renderlo simile al seguente:

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

Verranno ora esaminate più dettagliatamente diverse sezioni di questo codice. In primo luogo, se viene eseguita una nuova NSTableViewCell azione in base al nome della colonna. Per le prime due colonne (Product e Details), viene chiamato il nuovo ConfigureTextField metodo.

Per la colonna Azione , viene creato un nuovo NSButton oggetto e aggiunto alla cella come visualizzazione secondaria:

// 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 proprietà Button Tag viene utilizzata per contenere il numero della riga in fase di elaborazione. Questo numero verrà usato in un secondo momento quando l'utente richiede l'eliminazione di una riga nell'evento 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 ();
    }
  });
};

All'inizio del gestore eventi, si ottiene il pulsante e il prodotto che si trova nella riga della tabella specificata. Viene quindi visualizzato un avviso all'utente che conferma l'eliminazione della riga. Se l'utente sceglie di eliminare la riga, la riga specificata viene rimossa dall'origine dati e la tabella viene ricaricata:

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

Infine, se la cella di visualizzazione tabella viene riutilizzata invece di essere creata nuova, il codice seguente lo configura in base all'elaborazione della colonna:

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

Per la colonna Azione , tutte le visualizzazioni secondarie vengono analizzate fino a quando non NSButton viene trovato, quindi la proprietà viene Tag aggiornata in modo che punti alla riga corrente.

Con queste modifiche apportate, quando l'app viene eseguita ogni riga avrà un pulsante Elimina :

The table view with deletion buttons

Quando l'utente fa clic su un pulsante Elimina , verrà visualizzato un avviso che chiede di eliminare la riga specificata:

A delete row alert

Se l'utente sceglie l'eliminazione, la riga verrà rimossa e la tabella verrà ridisegnata:

The table after the row is deleted

Viste tabella di data binding

Usando le tecniche di codifica chiave-valore e data binding nell'applicazione Xamarin.Mac, è possibile ridurre notevolmente la quantità di codice che è necessario scrivere e gestire per popolare e usare gli elementi dell'interfaccia utente. È anche possibile separare ulteriormente i dati di backup (Modello di dati) dall'interfaccia utente front-end (Model-View-Controller), semplificando la gestione e la progettazione di applicazioni più flessibili.

Key-Value Coding (KVC) è un meccanismo per accedere indirettamente alle proprietà di un oggetto, usando chiavi (stringhe formattate appositamente) per identificare le proprietà anziché accedervi tramite variabili di istanza o metodi di accesso (get/set). Implementando funzioni di accesso conformi a Key-Value Coding nell'applicazione Xamarin.Mac, è possibile accedere ad altre funzionalità macOS, ad esempio Key-Value Observing (KVO), Data Binding, Core Data, Binding Cocoa e scriptability.

Per altre informazioni, vedere la sezione Associazione dati vista tabella della documentazione sul data binding e sulla codifica chiave-valore.

Riepilogo

Questo articolo ha esaminato in dettaglio l'uso delle viste tabella in un'applicazione Xamarin.Mac. Sono stati illustrati i diversi tipi e usi delle viste tabella, come creare e gestire viste tabella in Interface Builder di Xcode e come usare le viste tabella nel codice C#.