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 e Interface Builder e interactuar con ellas en el código.
Cuando se trabaja con C# y .NET en una aplicación de Xamarin.Mac, se tiene acceso a las mismas vistas de tabla que un desarrollador que trabaje en Objective-C y Xcode. Dado que Xamarin.Mac se integra directamente con Xcode, puede usar Interface Builder de Xcode para crear y mantener las vistas de tabla (u opcionalmente crearlas directamente en código C#).
Una vista de tabla muestra los datos en un formato tabular que contiene una o más columnas de información en varias filas. Según el tipo de vista de tabla que se va a crear, el usuario puede ordenar por columna, reorganizar columnas, agregar columnas, quitar columnas o editar los datos contenidos en la tabla.
En este artículo, trataremos los conceptos básicos del trabajo con vistas de tabla en una aplicación de Xamarin.Mac. Es muy recomendable leer primero el artículo Hello, Mac, en concreto las secciones Introducción a Xcode e Interface Builder y Salidas y acciones, ya que tratan los conceptos clave y las técnicas que se usan en este artículo.
También puede echar un vistazo a la sección Exponer clases o métodos de C# a Objective-C del documento sobre el funcionamiento interno de Xamarin.Mac, ya que en ella se explican los atributos Register
y Export
que se usan para conectar las clases de C# a objetos Objective-C y elementos de la interfaz de usuario.
Introducción a las vistas de tabla
Una vista de tabla muestra los datos en un formato tabular que contiene una o más columnas de información en varias filas. Las vistas de tabla se muestran dentro de las vistas de desplazamiento (NSScrollView
) y, a partir de macOS 10.7, se puede usar cualquier NSView
en lugar de celdas (NSCell
) para mostrar las filas y columnas. Dicho esto, se puede seguir usando NSCell
, pero lo habitual es subclasificar NSTableCellView
y crear filas y columnas personalizadas.
Una vista de tabla no almacena sus propios datos, sino que se basa en un origen de datos (NSTableViewDataSource
) para proporcionar las filas y columnas pertinentes según sea necesario.
El comportamiento de una vista de tabla se puede personalizar proporcionando una subclase del delegado de vista de tabla (NSTableViewDelegate
) para admitir la administración de columnas de tabla, la funcionalidad de tipo para seleccionar, la selección y edición de filas, el seguimiento personalizado y las vistas personalizadas para columnas y filas individuales.
Para crear vistas de tabla, Apple sugiere lo siguiente:
- Permitir al usuario ordenar la tabla haciendo clic en los encabezados de columna.
- Crear encabezados de columna que sean nombres o frases sustantivas cortas 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 de Apple.
Creación y mantenimiento de vistas de tabla en Xcode
Al crear una nueva aplicación de Cocoa de Xamarin.Mac, obtendrá una ventana estándar en blanco de forma predeterminada. Esta ventana se define en un archivo .storyboard
incluido automáticamente en el proyecto. Para editar el diseño de la ventana, vaya a Explorador de soluciones y haga doble clic en el archivo Main.storyboard
:
Se abrirá el diseño de la ventana en Interface Builder de Xcode:
Escriba table
en el cuadro de búsqueda del Inspector de biblioteca para facilitar la búsqueda de los controles de vista de tabla:
Arrastre una vista de tabla al controlador de vista del editor de interfaz, haga que rellene el área de contenido del controlador de vista y configúrela para que se reduzca y se agrande con la ventana en el editor de restricciones:
Seleccione la vista de tabla en la jerarquía de la interfaz: las siguientes propiedades estarán disponibles en el Inspector de atributos:
- Modo de contenido: permite usar vistas (
NSView
) o celdas (NSCell
) para mostrar los datos en las filas y columnas. A partir de macOS 10.7, debe usar vistas. - Hacer flotantes las celdas agrupadas: si es
true
, la vista de tabla dibujará las celdas agrupadas como si fueran flotantes. - Columnas: define el número de columnas mostradas.
- Encabezados: si es
true
, las columnas tendrán encabezados. - Reordenación: si es
true
, el usuario podrá reordenar las columnas de la tabla arrastrándolas. - Cambio de tamaño: si es
true
, el usuario podrá arrastrar los encabezados de columna para cambiar el tamaño de las columnas. - Tamaño de columna: controla la manera en que la tabla dimensionará automáticamente las columnas.
- Resaltar: controla el tipo de resaltado que se usa en la tabla cuando se selecciona una celda.
- Alternar filas: si es
true
, se aplicará un color de fondo diferente a las filas alternas. - Cuadrícula horizontal: selecciona el tipo de borde dibujado horizontalmente entre las celdas.
- Cuadrícula vertical: selecciona el tipo de borde dibujado verticalmente entre las celdas.
- Color de cuadrícula: establece el color del borde de celda.
- Fondo: establece el color de fondo de la celda.
- Selección: permite controlar cómo puede el usuario seleccionar celdas en la tabla de la siguiente forma:
- Múltiple: si es
true
, el usuario puede seleccionar varias filas y columnas. - Columna: si es
true
, el usuario puede seleccionar columnas. - Escribir para seleccionar, si es
true
, el usuario puede escribir un carácter para seleccionar una fila. - Vacío: si es
true
, no es necesario que el usuario seleccione una fila o columna, la tabla no permite ninguna selección.
- Múltiple: si es
- Guardar automáticamente: el nombre con el que se guarda automáticamente el formato de las tablas.
- Información de columna: si es
true
, 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
true
, la celda se truncará si los datos no caben dentro de sus límites.
Importante
A menos que esté manteniendo una aplicación heredada de Xamarin.Mac, se deben usar vistas de tabla basadas en NSView
en lugar de vistas de tabla basadas en NSCell
. NSCell
se considera heredado y es posible que no se admita en el futuro.
Seleccione una columna de tabla en la jerarquía de la interfaz: las siguientes propiedades estarán disponibles 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 de título: selecciona la fuente del texto de encabezado de la celda.
- Clave de ordenación: la clave que se usa para ordenar los datos de la columna. Dejar en blanco si el usuario no puede ordenar esta columna.
- Selector: la acción que se usa para realizar la ordenación. Dejar en blanco si el usuario no puede ordenar esta columna.
- Orden: el criterio de ordenación de los datos de columnas.
- Cambio de tamaño: selecciona el tipo de cambio de tamaño de la columna.
- Editable: si es
true
, el usuario puede editar las celdas en una tabla basada en celdas. - Oculta: si es
true
, la columna está oculta.
También puede cambiar el tamaño de la columna arrastrando el control (centrado verticalmente en el lado derecho de la columna) hacia la izquierda o la derecha.
Vamos a seleccionar cada columna de la vista de tabla y asignar a la primera columna el título Product
y a la segunda Details
.
Seleccione una vista de celda de tabla (NSTableViewCell
) en la jerarquía de la interfaz: las siguientes propiedades estarán disponibles en el Inspector de atributos:
Estas son todas las propiedades de una vista estándar. Aquí también tiene la opción de cambiar el tamaño de las filas de esta columna.
Seleccione una vista de celda de tabla (de forma predeterminadas, es un NSTextField
) en la jerarquía de la interfaz: las siguientes propiedades estarán disponibles en el Inspector de atributos:
Aquí podrá establecer todas las propiedades de un campo de texto estándar. 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 (NSTableFieldCell
) en la jerarquía de la interfaz: las siguientes propiedades estarán disponibles en el Inspector de atributos:
Las opciones más importantes son:
- Diseño: seleccione el diseño que tendrán las celdas de esta columna.
- Usa el modo de línea única: si es
true
, la celda se limita a una sola línea. - Ancho del diseño en la primera ejecución: si es
true
, la celda se ajusta al ancho establecido (ya sea manual o automáticamente) la primera vez que se ejecuta la aplicación. - Acción: controla cuando se envía la acción de edición de la celda.
- Comportamiento: define si una celda es seleccionable o editable.
- Texto enriquecido: si es
true
, la celda puede mostrar texto con formato y estilo. - Deshacer: si es
true
, la celda asume la responsabilidad del 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 la interfaz:
Esto le permite editar la vista celda de tabla usada como patrón base para todas las celdas creadas para la columna especificada.
Adición de acciones y salidas
Al igual que cualquier otro control de la interfaz de usuario de Cocoa, es necesario exponer la vista de tabla y sus columnas y celdas al código de C# mediante acciones y salidas (según la funcionalidad necesaria).
El proceso es el mismo para cualquier elemento de vista de tabla que deseemos exponer:
Cambie al Editor del Asistente y asegúrese de que el archivo
ViewController.h
esté seleccionado:Seleccione la vista de tabla en la jerarquía de la interfaz, haga clic manteniendo pulsada la tecla Control y arrástrela al archivo
ViewController.h
.Cree una salida para la vista de tabla denominada
ProductTable
:Cree también salidas para las columnas de tabla y denomínelas
ProductColumn
yDetailsColumn
:Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo 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 la vista de tabla diseñada en Interface Builder y expuesta a través de una salida, tenemos que crear el código de C# para rellenarla.
En primer lugar, vamos a crear una nueva clase Product
para almacenar la información de las filas individuales. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba Product
para el Nombre y haga clic en el botón Nuevo:
Edite el archivo Product.cs
para que quede de la siguiente manera:
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 el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba ProductTableDataSource
para el Nombre y haga clic en el botón Nuevo.
Edite el archivo ProductTableDataSource.cs
para que quede de la siguiente manera:
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 GetRowCount
para 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 el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba ProductTableDelegate
para el Nombre y haga clic en el botón Nuevo.
Edite el archivo ProductTableDelegate.cs
para que quede de la siguiente manera:
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 creamos una instancia de ProductTableDelegate
, también pasamos una instancia de ProductTableDataSource
que proporciona los datos de la tabla. El método GetViewForItem
es responsable de devolver una vista (datos) para mostrar la celda de una columna y una fila determinadas. Si es posible, se reutilizará una vista existente para mostrar la celda. De lo contrario, se debe crear una nueva vista.
Para rellenar la tabla, vamos a editar el archivo ViewController.cs
y modificar el AwakeFromNib
para que quede de la siguiente manera:
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:
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 clic en el archivo Main.storyboard
para abrirlo y editarlo en Interface Builder. Seleccione la columna Product
, escriba Title
para la Clave de ordenación y compare:
para el Selector y seleccione Ascending
para el Orden:
Seleccione la columna Details
, escriba Description
para la Clave de ordenación y compare:
para el Selector y seleccione Ascending
para el Orden:
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Ahora vamos a editar el archivo ProductTableDataSource.cs
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 Sort
nos permite ordenar los datos en el origen de datos en función de un campo de clase de Product
determinado en orden ascendente o descendente. Se llamará al método SortDescriptorsChanged
invalidado cada vez que el usuario haga clic en un encabezado de columna. Se pasará el valor de Clave establecido en Interface Builder y el criterio de ordenación de esa columna.
Si ejecutamos la aplicación y hacemos clic en un encabezado de columna, las filas se ordenarán por esa columna:
Selección de fila
Si quiere permitir que el usuario seleccione una sola fila, haga doble clic en el archivo Main.storyboard
para abrirlo y editarlo en Interface Builder. Seleccione la vista de tabla en la jerarquía de la interfaz y desactive la casilla Múltiple en el Inspector de atributos:
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
A continuación, edite el archivo ProductTableDelegate.cs
y agregue el método siguiente:
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
Esto permitirá al usuario seleccionar cualquier fila individual en la vista de tabla. Vuelva false
para cualquier ShouldSelectRow
fila que no quiera que el usuario pueda seleccionar o false
para cada fila si no desea que el usuario pueda seleccionar ninguna 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 una fila dada en la tabla.SelectRow(nint,bool)
: selecciona una fila dada. Pasefalse
para el segundo parámetro para seleccionar solo una fila cada vez.SelectedRow
: devuelve la fila actual seleccionada en la tabla.IsRowSelected(nint)
: devuelvetrue
si se selecciona la fila dada.
Selección de varias filas
Si quiere permitir que el usuario seleccione varias filas, haga doble clic en el archivo Main.storyboard
para abrirlo y editarlo en Interface Builder. Seleccione la vista de tabla en la jerarquía de la interfaz y active la casilla Múltiple en el Inspector de atributos:
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
A continuación, edite el archivo ProductTableDelegate.cs
y agregue el método siguiente:
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
Esto permitirá al usuario seleccionar cualquier fila individual en la vista de tabla. Vuelva false
para cualquier ShouldSelectRow
fila que no quiera que el usuario pueda seleccionar o false
para cada fila si no desea que el usuario pueda seleccionar ninguna 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. Usethis
como primer parámetro para enviar el objeto que realiza la selección.DeselectRow(nint)
: anula la selección de una fila dada en la tabla.SelectAll(NSobject)
: selecciona todas las filas de la tabla. Usethis
como primer parámetro para enviar el objeto que realiza la selección.SelectRow(nint,bool)
: selecciona una fila dada. Si se pasafalse
para el segundo parámetro se borra la selección y se selecciona solo una fila, si se pasatrue
se amplía la selección y se incluye esta fila.SelectRows(NSIndexSet,bool)
: selecciona el conjunto de filas dado. Si se pasafalse
para el segundo parámetro se borra la selección y se seleccionan solo estas filas, si se pasatrue
se amplía la selección y se incluyen estas filas.SelectedRow
: devuelve la fila actual seleccionada en la tabla.SelectedRows
: devuelve unNSIndexSet
que contiene los índices de las filas seleccionadas.SelectedRowCount
: devuelve el número de filas seleccionadas.IsRowSelected(nint)
: devuelvetrue
si se selecciona la fila dada.
Escribir para seleccionar fila
Si quiere que el usuario pueda escribir un carácter con la vista de tabla seleccionada y seleccionar la primera fila que contenga ese carácter, haga doble clic en el archivo Main.storyboard
para abrirlo y editarlo en Interface Builder. Seleccione la vista de tabla en la jerarquía de la interfaz y active la casilla Escribir para seleccionar en el Inspector de atributos:
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Ahora vamos a editar el archivo ProductTableDelegate.cs
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 método GetNextTypeSelectMatch
toma la searchString
dada y devuelve la fila del primer Product
que tenga esa cadena en su Title
.
Si ejecutamos la aplicación y escribimos un carácter, se selecciona una fila:
Reordenación de columnas
Si quiere permitir que el usuario arrastre las columnas para reordenarlas en la vista de tabla, haga doble clic en el archivo Main.storyboard
para abrirlo y editarlo en Interface Builder. Seleccione la vista de tabla en la jerarquía de la interfaz y active la casilla Reordenar en el Inspector de atributos:
Si se proporciona un valor para la propiedad Guardar automáticamente y se activa el campo Información de columna, los cambios realizados 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 sincronizarlo con Xcode.
Ahora vamos a editar el archivo ProductTableDelegate.cs
y agregar el método siguiente:
public override bool ShouldReorder (NSTableView tableView, nint columnIndex, nint newColumnIndex)
{
return true;
}
El método ShouldReorder
debe devolver true
para cualquier columna que se pueda arrastrar para reordenar en newColumnIndex
, en caso contrario devolverá false
.
Si ejecutamos la aplicación, podemos arrastrar los encabezados de columna para reordenar las columnas:
Edición de celdas
Si quiere permitir que el usuario edite los valores de una celda determinada, edite el archivo ProductTableDelegate.cs
y cambie el método GetViewForItem
de la siguiente manera:
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 de tabla:
Uso de imágenes en vistas de tabla
Para incluir una imagen como parte de la celda en , NSTableView
deberá cambiar cómo devuelven los datos el método de NSTableViewDelegate's
GetViewForItem
la vista de tabla para usar en NSTableCellView
lugar del típico NSTextField
. 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 la documentación de trabajo con imágenes.
Adición de un botón Eliminar a una fila
Según 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. Como ejemplo, vamos a ampliar el ejemplo de vista de tabla creado anteriormente para incluir un botón Eliminar en cada fila.
En primer lugar, edite Main.storyboard
en Interface Builder de Xcode, seleccione la vista de tabla y aumente el número de columnas a tres (3). A continuación, cambie el Título de la nueva columna a Action
:
Guarde los cambios en el guion gráfico y vuelva a Visual Studio para Mac para sincronizar los cambios.
A continuación, edite el archivo ViewController.cs
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 ViewDidLoad
de la siguiente manera:
// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (this, DataSource);
Ahora, edite el archivo ProductTableDelegate.cs
para incluir una conexión privada al controlador de vista y tomar el controlador como parámetro al crear una nueva 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 realizando anteriormente en el método GetViewForItem
y las coloca en una sola ubicación invocable (ya que la última columna de la tabla no incluye una vista de texto, sino un botón).
Por último, edite el método GetViewForItem
para que quede de la siguiente manera:
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;
}
Veamos con más detalle varias secciones de este código. En primer lugar, si se crea una nueva NSTableViewCell
se realiza una acción en función del nombre de la columna. Para las dos primeras columnas (Product y Details), se llama al nuevo método ConfigureTextField
.
Para la columna Action, se crea un nuevo NSButton
y se agrega a la celda como una vista secundaria:
// 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 Tag
del botón se usa para guardar 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 Activated
del 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 inicio del controlador de eventos, obtenemos el botón y el producto que se encuentra en la fila de tabla dada. A continuación, se presenta una alerta al usuario que confirma la eliminación de la fila. Si el usuario elige eliminar la fila, se quita la fila dada 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 se vuelve a usar la celda de vista de tabla en lugar de crear una nueva, el código siguiente lo 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;
}
Para la columna Action, se examinan todas las vistas secundarias hasta encontrar NSButton
y, luego, se actualiza la propiedad Tag
para que apunte a la fila actual.
Con estos cambios implementados, cada fila tendrá un botón Eliminar cuando se ejecute la aplicación:
Cuando el usuario haga clic en un botón Eliminar, se mostrará una alerta en la que se le preguntará si desea eliminar la fila en cuestión:
Si el usuario elige eliminarla, se quitará la fila y se volverá a dibujar la tabla:
Vistas de tabla de enlace de datos
Mediante el uso de técnicas de codificación de clave-valor y enlace de datos en la aplicación de 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 poder desacoplar aún más los datos de respaldo (Modelo de datos) de la interfaz de usuario de front-end (Modelo-Vista-Controlador), lo que facilita el mantenimiento y hace que el diseño de las aplicaciones sea más flexible.
La codificación de clave-valor (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 acceder a ellas a través de variables de instancia o métodos de descriptor de acceso (get/set
). Al implementar descriptores de acceso compatibles con la codificación de clave-valor en la aplicación de Xamarin.Mac, obtendrá acceso a otras características de macOS, como la observación de clave-valor (KVO), el enlace de datos, Core Data, enlaces de Cocoa y la capacidad de ejecución mediante scripts.
Para obtener más información, consulte la sección Enlace de datos de vista de tabla de nuestra documentación sobre el enlace de datos y la codificación de clave-valor.
Resumen
En este artículo se trata en detalle cómo trabajar con vistas de tabla en una aplicación de Xamarin.Mac. Se explican los distintos tipos y usos de las vistas de tabla, cómo crear y mantener vistas de tabla en Interface Builder de Xcode y cómo trabajar con vistas de tabla en código de C#.
Vínculos relacionados
- Hello, Mac
- Vistas de esquema
- Listas de origen
- Enlace de datos y codificación de clave-valor
- OS X Human Interface Guidelines (Directrices de interfaz humana de OS X)
- NSTableView
- NSTableViewDelegate
- NSTableViewDataSource