Vistas de tabla en Xamarin.Mac

En este artículo se explica cómo trabajar con vistas de tabla en una aplicación de Xamarin.Mac. Describe cómo crear vistas de tabla en Xcode Interface Builder e interactuar con ellas en el código.

Al trabajar con C# y .NET en una aplicación de Xamarin.Mac, tiene acceso a las mismas vistas de tabla en las que trabaja un desarrollador y Objective-CObjective-C Dado que Xamarin.Mac se integra directamente con Xcode, puede usar el Interface Builder de Xcode para crear y mantener las vistas de tabla (o, opcionalmente, crearlas directamente en código de C#).

Una vista de tabla muestra los datos en un formato tabular que contiene una o varias columnas de información en varias filas. En función del tipo de vista de tabla que se crea, el usuario puede ordenar por columna, reorganizar columnas, agregar columnas, quitar columnas o editar los datos contenidos en la tabla.

Una tabla de ejemplo

En este artículo, se tratarán los conceptos básicos de trabajar con vistas de tabla en una aplicación de Xamarin.Mac. Se recomienda encarecidamente que trabaje primero en el artículo Hello, Mac, en concreto en las secciones Introduction to Xcode and Interface Builder and Outlets and Actions (Introducción a Xcode y Interface Builder y salidas y acciones), ya que trata los conceptos y técnicas clave que se usarán en este artículo.

También puede echar un vistazo a la sección del documento Exposing C# classes / methods to Objective-CExposing C# classes / methods to Objective-C internos de Xamarin.Mac), donde se explican los comandos y que se usan para conectar las clases de C# a objetos y elementos de interfaz de RegisterExportObjective-C usuario.

Introducción a las vistas de tabla

Una vista de tabla muestra los datos en un formato tabular que contiene una o varias columnas de información en varias filas. Las vistas de tabla se muestran dentro de vistas de desplazamiento ( ) y, a partir de NSScrollView macOS 10.7, puede usar cualquier en lugar de Celdas ( ) para mostrar filas NSViewNSCell y columnas. Dicho esto, todavía puede usar, pero normalmente creará NSCell subclase NSTableCellView y creará las filas y columnas personalizadas.

Una vista de tabla no almacena sus propios datos, sino que se basa en un origen de datos ( ) para proporcionar las filas y columnas necesarias, según sea NSTableViewDataSource necesario.

El comportamiento de una vista de tabla se puede personalizar proporcionando una subclase del delegado de vista de tabla ( ) para admitir la administración de columnas de tabla, escriba para seleccionar la funcionalidad, la selección y edición de filas, el seguimiento personalizado y las vistas personalizadas para columnas y filas NSTableViewDelegate individuales.

Al crear vistas de tabla, Apple sugiere lo siguiente:

  • Permita al usuario ordenar la tabla haciendo clic en encabezados de columna.
  • Cree encabezados de columna que sean sustantivos o frases de nombre cortos que describan los datos que se muestran en esa columna.

Para obtener más información, consulte la sección Vistas de contenido de las directrices de interfaz humana de OS X deApple.

Creación y mantenimiento de vistas de tabla en Xcode

Cuando se crea una nueva aplicación Xamarin.Mac Cocoa, se obtiene una ventana estándar en blanco de forma predeterminada. Esta ventana se define en un .storyboard archivo incluido automáticamente en el proyecto. Para editar el diseño de windows, en la Explorador de soluciones, haga doble clic en el archivo:

Selección del guión gráfico principal

Se abrirá el diseño de la ventana en la ventana de Xcode Interface Builder:

Edición de la interfaz de usuario en Xcode

Escriba en el cuadro de búsqueda del inspector de biblioteca para facilitar table la búsqueda de los controles de vista de tabla: table

Selección de una vista de tabla de la biblioteca

Arrastre una vista de tabla al controlador de vistas en el Editorde interfaz , haga que rellene el área de contenido del controlador de vistas y esta establezca en donde se reduce y crece con la ventana en el Editor de restricciones:

Restricciones de edición

Seleccione la Vista de tabla en la jerarquía de interfaz y las siguientes propiedades están disponibles en el Inspector de atributos:

Captura de pantalla que muestra las propiedades disponibles en el Inspector de atributos.

  • Modo de contenido: permite usar vistas ( ) o celdas ( ) para mostrar NSCell los datos en las filas y columnas. A partir de macOS 10.7, debe usar Vistas.
  • Filas de grupo floats: si es , la vista Tabla dibujará celdas agrupadas como si hubieran estado flotantes.
  • Columnas: define el número de columnas que se muestran.
  • Encabezados: si es , las columnas tendrán encabezados.
  • Reordenación: si es , el usuario podrá arrastrar para volver a ordenar las columnas de la tabla.
  • Cambio de tamaño: si es , el usuario podrá arrastrar los encabezados de columna para cambiar el tamaño de las columnas.
  • Ajuste de tamaño de columna: controla cómo la tabla dimensiona automáticamente las columnas.
  • Resaltar: controla el tipo de resaltado que usa la tabla cuando se selecciona una celda.
  • Filas alternativas: si es , cualquier otra fila tendrá un color de fondo diferente.
  • Cuadrícula horizontal: selecciona el tipo de borde dibujado entre las celdas horizontalmente.
  • Cuadrícula vertical: selecciona el tipo de borde dibujado entre celdas verticalmente.
  • Color de cuadrícula: establece el color del borde de la celda.
  • Fondo: establece el color de fondo de la celda.
  • Selección: permite controlar cómo el usuario puede seleccionar las celdas de la tabla como:
    • Varios: si es , el usuario puede seleccionar varias filas y columnas.
    • Columna: si es , el usuario puede seleccionar columnas.
    • Seleccionar tipo: si es , el usuario puede escribir un carácter para seleccionar una fila.
    • Vacío: si , el usuario no tiene que seleccionar una fila o columna, la tabla no permite ninguna selección.
  • Autoguardar: el nombre en el que se guarda automáticamente el formato de las tablas.
  • Información de columna: si es , el orden y el ancho de las columnas se guardarán automáticamente.
  • Saltos de línea: seleccione cómo controla la celda los saltos de línea.
  • Trunca la última línea visible: si es , la celda se truncará en los datos que no caben dentro de sus límites.

Importante

A menos que mantenga una aplicación de Xamarin.Mac heredada, las vistas de tabla basadas se NSView deben usar en vistas de tabla NSCell basadas. NSCell se considera heredado y es posible que no se puedan usar en el futuro.

Seleccione una columna de tabla en la jerarquía de interfaz y las siguientes propiedades están disponibles en el Inspector de atributos:

Captura de pantalla que muestra las propiedades disponibles para una columna de tabla en el Inspector de atributos.

  • Título: establece el título de la columna.
  • Alineación: establece la alineación del texto dentro de las celdas.
  • Fuente del título: selecciona la fuente del texto de encabezado de la celda.
  • Clave de ordenación: es la clave que se usa para ordenar los datos de la columna. Déjelo en blanco si el usuario no puede ordenar esta columna.
  • Selector: es la acción que se usa para realizar la ordenación. Déjelo en blanco si el usuario no puede ordenar esta columna.
  • Order: es el criterio de ordenación de los datos de las columnas.
  • Redimensionar: selecciona el tipo de tamaño de la columna.
  • Editable: si es , el usuario puede editar las celdas de una tabla basada en celdas.
  • Oculto: si es , la columna está oculta.

También puede cambiar el tamaño de la columna arrastrando su identificador (centrado verticalmente en el lado derecho de la columna) a la izquierda o derecha.

Vamos a seleccionar cada columna de nuestra vista de tabla y a dar a la primera columna un título de y a la segunda. Details

Seleccione una vista de celda de tabla ( ) en la jerarquía de interfaz y NSTableViewCell las siguientes propiedades están disponibles en el Inspector de atributos: NSTableViewCell

Captura de pantalla que muestra las propiedades disponibles para una vista de celda de tabla en el Inspector de atributos.

Estas son todas las propiedades de una vista estándar. También tiene la opción de volver a tamaño de las filas de esta columna aquí.

Seleccione una celda de vista de tabla (de forma predeterminada, es ) en la jerarquía de interfaz y las siguientes propiedades están NSTextField disponibles en el Inspector de atributos: NSTextField

Captura de pantalla que muestra las propiedades disponibles para una celda de vista de tabla en el Inspector de atributos.

Tendrá todas las propiedades de un campo de texto estándar que se establecerá aquí. De forma predeterminada, se usa un campo de texto estándar para mostrar los datos de una celda de una columna.

Seleccione una vista de celda de tabla ( ) en la jerarquía de interfaz y NSTableFieldCell las siguientes propiedades están disponibles en el Inspector de atributos: NSTableFieldCell

Captura de pantalla que muestra las propiedades disponibles para otra celda de vista de tabla en el Inspector de atributos.

La configuración más importante aquí es:

  • Diseño: seleccione cómo se disposición de las celdas de esta columna.
  • Usa el modo de línea única: si es , la celda se limita a una sola línea.
  • Primer ancho de diseño en tiempo de ejecución: si es , la celda preferirá el ancho establecido para él (ya sea manualmente o automáticamente) cuando se muestre la primera vez que se ejecute la aplicación.
  • Acción: controla cuándo se envía la acción editar para la celda.
  • Comportamiento: define si una celda se puede seleccionar o editar.
  • Texto enriquecido: si es , la celda puede mostrar texto con formato y estilo.
  • Deshacer: si es , la celda asume la responsabilidad de su comportamiento de deshacer.

Seleccione la Vista de celda de tabla ( NSTableFieldCell ) en la parte inferior de una columna de tabla en la jerarquía de NSTableFieldCell:

Selección de la vista celda de tabla

Esto le permite editar la vista de celda de tabla que se usa como patrón base para todas las celdas creadas para la columna especificada.

Agregar acciones y salidas

Al igual que cualquier otro control de la interfaz de usuario de Cocoa, es necesario exponer nuestra vista de tabla y sus columnas y celdas en el código de C# mediante acciones y salidas (en función de la funcionalidad necesaria).

El proceso es el mismo para cualquier elemento table view que se quiera exponer:

  1. Cambie al Editor del asistente y asegúrese de que el archivo está seleccionado:

    Editor del asistente

  2. Seleccione la Vista de tabla en la jerarquía de interfaces,haga clic con el botón derecho en el control y arrástrela al archivo.

  3. Cree una salida para la vista de tabla denominada :

    Captura de pantalla que muestra una conexión de salida creada para la vista de tabla denominada ProductTable.

  4. Cree salidas para las columnas de tablas también denominadas y DetailsColumn :

    Captura de pantalla que muestra las conexiones de salida creadas para otras vistas de tabla.

  5. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

A continuación, escribiremos el código para mostrar algunos datos de la tabla cuando se ejecute la aplicación.

Rellenar la vista de tabla

Con nuestra vista de tabla diseñada en Interface Builder y expuesta a través de una salida ,a continuación, es necesario crear el código de C# para rellenarlo.

En primer lugar, vamos a crear una nueva Product clase para contener la información de las filas individuales. En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo):

Creación de una clase vacía

Haga que Product.cs el archivo sea parecido al siguiente:

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

A continuación, es necesario crear una subclase de NSTableDataSource para proporcionar los datos de la tabla tal como se solicita. En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo).

Edite ProductTableDataSource.cs el archivo y haga que sea parecido al siguiente:

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

Esta clase tiene almacenamiento para los elementos de la vista de tabla e invalida para GetRowCount devolver el número de filas de la tabla.

Por último, es necesario crear una subclase de NSTableDelegate para proporcionar el comportamiento de la tabla. En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo).

Edite ProductTableDelegate.cs el archivo y haga que sea parecido al siguiente:

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

Cuando se crea una instancia de , también se pasa una instancia ProductTableDelegate de que proporciona los datos de la ProductTableDataSource tabla. El GetViewForItem método es responsable de devolver una vista (datos) para mostrar la celda de una columna y fila de tipo give. Si es posible, se reutilizará una vista existente para mostrar la celda, si no se debe crear una nueva vista.

Para rellenar la tabla, vamos a editar el ViewController.cs archivo y hacer que el método sea parecido al AwakeFromNib siguiente:

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 ejecutamos la aplicación, se muestra lo siguiente:

Captura de pantalla que muestra una ventana denominada Tabla de productos con tres entradas.

Ordenación por columna

Vamos a permitir que el usuario ordene los datos de la tabla haciendo clic en un encabezado de columna. En primer lugar, haga doble Main.storyboard clic en el archivo para abrirlo para editarlo en Interface Builder. Seleccione la Product columna , escriba para la clave de TitleProduct, compare: para el Title y seleccione para Ascending el compare::

Captura de pantalla que Interface Builder donde puede establecer la clave de ordenación para la columna Product.

Seleccione la Details columna , escriba para la clave de DescriptionDetails, compare: para el Description y seleccione para Ascending el compare::

Captura de pantalla que Interface Builder donde puede establecer la clave de ordenación para la columna Detalles.

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

Ahora vamos a editar el ProductTableDataSource.cs archivo y agregar los métodos siguientes:

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

El método nos permite ordenar los datos en el origen de datos en función de un campo de clase determinado en orden ascendente SortProduct o descendente. Se llamará al SortDescriptorsChanged método invalidado cada vez que el uso haga clic en un encabezado de columna. Se le pasará el valor de clave que establecimos en Interface Builder y el criterio de ordenación de esa columna.

Si ejecutamos la aplicación y hacemos clic en los encabezados de columna, las filas se ordenarán por esa columna:

Ejecución de una aplicación de ejemplo

Selección de filas

Si desea permitir que el usuario seleccione una sola fila, haga doble clic en el archivo para abrirlo para editarlo Main.storyboard en Interface Builder. Seleccione la Vista de tabla en la jerarquía de interfaces y desactive la casilla Varios en el Inspector de atributos:

Captura de pantalla que Interface Builder donde puede seleccionar Varios en el Inspector de atributos.

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

A continuación, ProductTableDelegate.cs edite el archivo y agregue el método siguiente:

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

Esto permitirá al usuario seleccionar cualquier fila única en la vista tabla. Vuelva para cualquier fila que no desee que el usuario pueda seleccionar o para cada fila si no desea que el usuario pueda seleccionar falseShouldSelectRow ninguna false fila.

La vista de tabla ( NSTableView ) contiene los métodos siguientes para trabajar con la selección de filas:

  • DeselectRow(nint) : anula la selección de la fila dada en la tabla.
  • SelectRow(nint,bool) : selecciona la fila dada. Pase false para que el segundo parámetro seleccione solo una fila a la vez.
  • SelectedRow : devuelve la fila actual seleccionada en la tabla.
  • IsRowSelected(nint) : devuelve true si se selecciona la fila determinada.

Selección de varias filas

Si desea permitir que el usuario seleccione varias filas, haga doble clic en el archivo para abrirlo para editarlo Main.storyboard en Interface Builder. Seleccione la Vista de tabla en la jerarquía de interfaces y active la casilla Varios en el Inspector de atributos:

Captura de pantalla que Interface Builder donde puede seleccionar Varios para permitir la selección de varias filas.

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

A continuación, ProductTableDelegate.cs edite el archivo y agregue el método siguiente:

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

Esto permitirá al usuario seleccionar cualquier fila única en la vista tabla. Vuelva para cualquier fila que no desee que el usuario pueda seleccionar o para cada fila si no desea que el usuario pueda seleccionar falseShouldSelectRow ninguna false fila.

La vista de tabla ( NSTableView ) contiene los métodos siguientes para trabajar con la selección de filas:

  • DeselectAll(NSObject) : anula la selección de todas las filas de la tabla. Use this para el primer parámetro que se va a enviar en el objeto que hace la selección.
  • DeselectRow(nint) : anula la selección de la fila dada en la tabla.
  • SelectAll(NSobject) : selecciona todas las filas de la tabla. Use this para el primer parámetro que se va a enviar en el objeto que hace la selección.
  • SelectRow(nint,bool) : selecciona la fila dada. Pase para el segundo parámetro borre la selección y seleccione solo una fila, pase para false ampliar la selección e incluya esta true fila.
  • SelectRows(NSIndexSet,bool) : selecciona el conjunto de filas determinado. Pase para el segundo parámetro borre la selección y seleccione solo una de estas filas, pase para false ampliar la selección e incluir estas true filas.
  • SelectedRow : devuelve la fila actual seleccionada en la tabla.
  • SelectedRows : devuelve un NSIndexSet objeto que contiene los índices de las filas seleccionadas.
  • SelectedRowCount : devuelve el número de filas seleccionadas.
  • IsRowSelected(nint) : devuelve true si se selecciona la fila determinada.

Tipo para seleccionar fila

Si desea permitir que el usuario escriba un carácter con la Vista de tabla seleccionada y seleccione la primera fila que tenga ese carácter, haga doble clic en el archivo para abrirlo para editarlo en Main.storyboard Interface Builder. Seleccione la Vista de tabla en la jerarquía de interfaces y active la casilla Seleccionar tipo en el Inspector de atributos:

Establecimiento del tipo de selección

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

Ahora vamos a editar el ProductTableDelegate.cs archivo y agregar el método siguiente:

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

El GetNextTypeSelectMatch método toma el objeto dado y devuelve la fila del primero que tiene esa cadena en su searchStringProduct . Title

Si ejecutamos la aplicación y escribemos un carácter, se selecciona una fila:

Captura de pantalla que muestra el resultado de ejecutar la aplicación.

Reordenación de columnas

Si desea permitir que el usuario arrastre las columnas de reordenación en la vista tabla, haga doble clic en el archivo para abrirlo para editarlo en Main.storyboard Interface Builder. Seleccione la Vista de tabla en la jerarquía de interfaces y active la casilla Reordenar en el Inspector de atributos:

Captura de pantalla que Interface Builder donde puede seleccionar Volver a usar en el Inspector de atributos.

Si se proporciona un valor para la propiedad Autosave y se comprueba el campo Información de columna, los cambios que realicemos en el diseño de la tabla se guardarán automáticamente y se restaurarán la próxima vez que se ejecute la aplicación.

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

Ahora vamos a editar el ProductTableDelegate.cs archivo y agregar el método siguiente:

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

El método debe devolver para cualquier columna que quiera permitir que se arrastre ShouldReordertrue reordenada a newColumnIndex , de lo contrario, devuelve false ;

Si ejecutamos la aplicación, podemos arrastrar encabezados de columna para reordenar nuestras columnas:

Ejemplo de las columnas reordenadas

Edición de celdas

Si desea permitir que el usuario edite los valores de una celda determinada, edite el archivo y cambie el ProductTableDelegate.cs método como se muestra a GetViewForItem continuación:

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

Ahora, si ejecutamos la aplicación, el usuario puede editar las celdas en la vista tabla:

Ejemplo de edición de una celda

Usar imágenes en vistas de tabla

Para incluir una imagen como parte de la celda en , deberá cambiar la forma en que el método de la vista de tabla devuelve los datos para usar un en lugar del típico NSTableViewNSTableViewDelegate'sGetViewForItemNSTableCellViewNSTextField . Por ejemplo:

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

Para obtener más información, consulte la sección Uso de imágenes con vistas de tabla de nuestra documentación sobre cómo trabajar con imágenes.

Agregar un botón Eliminar a una fila

En función de los requisitos de la aplicación, puede haber ocasiones en las que necesite proporcionar un botón de acción para cada fila de la tabla. Por ejemplo, vamos a expandir el ejemplo de vista de tabla creado anteriormente para incluir un botón Eliminar en cada fila.

En primer lugar, edite en la página de Interface Builder Xcode, seleccione la Vista de tabla y aumente el número de columnas Main.storyboard a tres (3). A continuación, cambie el Título de la nueva columna a :

Edición del nombre de columna

Guarde los cambios en el guión gráfico y vuelva a Visual Studio para Mac para sincronizar los cambios.

A continuación, ViewController.cs edite el archivo y agregue el siguiente método público:

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

En el mismo archivo, modifique la creación del nuevo delegado de vista de tabla dentro del método como ViewDidLoad se muestra a continuación:

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

Ahora, edite el archivo para incluir una conexión privada al controlador de vista y tomar el controlador como parámetro al crear una nueva ProductTableDelegate.cs instancia del delegado:

#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

A continuación, agregue el siguiente nuevo método privado a la clase :

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

Esto toma todas las configuraciones de vista de texto que se estaban haciendo anteriormente en el método y las coloca en una única ubicación a la que se puede llamar (puesto que la última columna de la tabla no incluye una vista de texto, sino un GetViewForItem botón).

Por último, GetViewForItem edite el método y haga que sea parecido al siguiente:

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

Echemos un vistazo a varias secciones de este código con más detalle. En primer lugar, si se NSTableViewCell crea una nueva acción se toma en función del nombre de la columna. Para las dos primeras columnas(Product y Details),se llama al nuevo método .

Para la columna Acción, se crea un nuevo objeto y se agrega a la celda como subconsulta:

// 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 propiedad button se Tag usa para contener el número de la fila que se está procesando actualmente. Este número se usará más adelante cuando el usuario solicite que se elimine una fila en el evento del Activated botón:

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

Al principio del controlador de eventos, se obtiene el botón y el producto que se encuentra en la fila de tabla determinada. A continuación, se presenta una alerta al usuario que confirma la eliminación de la fila. Si el usuario decide eliminar la fila, la fila dada se quita del origen de datos y se vuelve a cargar la tabla:

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

Por último, si la celda de vista de tabla se reutiliza en lugar de crearse como nueva, el código siguiente la configura en función de la columna que se está procesando:

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

En la columna Acción, se examinan todas las subconsidciones hasta que se encuentra y, a continuación, su propiedad se actualiza para que apunte a la fila Tag actual.

Con estos cambios en su lugar, cuando se ejecuta la aplicación, cada fila tendrá un botón Eliminar:

Vista de tabla con botones de eliminación

Cuando el usuario hace clic en un botón Eliminar, se mostrará una alerta en la que se le pedirá que elimine la fila dada:

Una alerta de eliminación de fila

Si el usuario elige eliminar, se quitará la fila y se volverá a dibujar la tabla:

Tabla después de eliminar la fila

Vistas de tabla de enlace de datos

Al usar Key-Value codificación y enlace de datos en la aplicación Xamarin.Mac, puede reducir considerablemente la cantidad de código que tiene que escribir y mantener para rellenar y trabajar con elementos de la interfaz de usuario. También tiene la ventaja de desacomanar aún más los datos de respaldo(modelode datos) de su front-end Interfaz de usuario(modelo-vista-controlador),lo que facilita el mantenimiento y el diseño de aplicaciones más flexible.

Key-Value Coding (KVC) es un mecanismo para acceder indirectamente a las propiedades de un objeto, mediante claves (cadenas con formato especial) para identificar propiedades en lugar de tener acceso a ellas a través de variables de instancia o métodos de accessor ( get/set ). Al implementar Key-Value de acceso compatibles con codificación en la aplicación Xamarin.Mac, se obtiene acceso a otras características de macOS, como Key-Value Observing (KVO), enlace de datos, datos principales, enlaces de Cocoa y capacidad de script.

Para obtener más información, consulte la sección Enlace de datos de la vista de tabla de nuestra documentación sobre enlace de datos Key-Value codificación.

Resumen

En este artículo se ha detallado cómo trabajar con vistas de tabla en una aplicación de Xamarin.Mac. Vimos los diferentes tipos y usos de vistas de tabla, cómo crear y mantener vistas de tabla en el Interface Builder de Xcode y cómo trabajar con vistas de tabla en código de C#.