Enlace de datos y codificación de clave-valor en Xamarin.Mac
En este artículo, se describe el uso de la codificación de clave-valor y la observación de clave-valor para permitir el enlace de datos a los elementos de la interfaz de usuario en Interface Builder de Xcode.
Información general
Al trabajar con C# y .NET en una aplicación de Xamarin.Mac, tendrá acceso a las mismas técnicas de codificación clave-valor y de enlace de datos que un desarrollador que trabaje en Objective-C y Xcode. Como Xamarin.Mac se integra directamente con Xcode, puede usar Interface Builder de Xcode para enlazar datos con elementos de la interfaz de usuario en lugar de escribir código.
Mediante el uso de técnicas de codificación de clave-valor y de 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.
En este artículo, abordaremos los conceptos básicos sobre el trabajo con codificación de clave-valor y enlace de datos en una aplicación de Xamarin.Mac. Se recomienda encarecidamente primero revisar el artículo Hello, Mac; específicamente las secciones Introducción a Xcode e Interface Builder y Salidas y acciones, ya que se abordan conceptos clave y técnicas que usaremos en este artículo.
Es posible que también deba consultar la sección Exponer clases o métodos de C# a Objective-C del documento Xamarin.Mac Internals, ya que explica los atributos Register
y Export
que se usan para conectar las clases de C# a objetos y elementos de la interfaz de usuario de Objective-C.
¿Qué es la codificación de clave-valor?
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 (antes conocido como OS X), 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.
Mediante el uso de técnicas de codificación de clave-valor y de 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.
Por ejemplo, echemos un vistazo a la siguiente definición de clase de un objeto compatible con KVC:
using System;
using Foundation;
namespace MacDatabinding
{
[Register("PersonModel")]
public class PersonModel : NSObject
{
private string _name = "";
[Export("Name")]
public string Name {
get { return _name; }
set {
WillChangeValue ("Name");
_name = value;
DidChangeValue ("Name");
}
}
public PersonModel ()
{
}
}
}
En primer lugar, el atributo [Register("PersonModel")]
registra la clase y la expone a Objective-C. Después, la clase necesita heredar de NSObject
(o una subclase que herede de NSObject
), esto agrega varios métodos base que permiten a la clase ser compatible con KVC. A continuación, el atributo [Export("Name")]
expone la propiedad Name
y define el valor de la clave que se usará posteriormente para acceder a la propiedad mediante las técnicas KVC y KVO.
Por último, para poder ser clave-valor observado los cambios en el valor de la propiedad, el descriptor de acceso debe encapsular los cambios en su valor en las llamadas a los métodos WillChangeValue
y DidChangeValue
(especificando la misma clave que el atributo Export
). Por ejemplo:
set {
WillChangeValue ("Name");
_name = value;
DidChangeValue ("Name");
}
Este paso es muy importante para enlazar datos en Interface Builder de Xcode (como veremos más adelante en este artículo).
Para más información, consulte la Guía de programación de codificación clave-valor de Apple.
Claves y rutas de acceso de clave
Una clave es una cadena que identifica una propiedad específica de un objeto. Normalmente, una clave corresponde al nombre de un método de descriptor de acceso en un objeto compatible con codificación de clave-valor. Las claves deben usar la codificación ASCII, suelen empezar por una letra minúscula y no pueden contener espacios en blanco. Así, dado el ejemplo anterior, Name
sería un valor clave de la propiedad Name
de la clase PersonModel
. La clave y el nombre de la propiedad que exponen no tienen que ser iguales, pero en la mayoría de los casos son.
Una Ruta de acceso de clave es una cadena de claves separadas por puntos que se usa para especificar una jerarquía de propiedades de objetos que recorrer. La propiedad de la primera clave de la secuencia es relativa al receptor, y cada clave posterior se evalúa en relación con el valor de la propiedad anterior. De la misma forma que se usa la notación de puntos para recorrer un objeto y sus propiedades en una clase C#.
Por ejemplo, si expande la clase PersonModel
y agrega la propiedad Child
:
using System;
using Foundation;
namespace MacDatabinding
{
[Register("PersonModel")]
public class PersonModel : NSObject
{
private string _name = "";
private PersonModel _child = new PersonModel();
[Export("Name")]
public string Name {
get { return _name; }
set {
WillChangeValue ("Name");
_name = value;
DidChangeValue ("Name");
}
}
[Export("Child")]
public PersonModel Child {
get { return _child; }
set {
WillChangeValue ("Child");
_child = value;
DidChangeValue ("Child");
}
}
public PersonModel ()
{
}
}
}
La ruta de acceso de clave al nombre del elemento secundario sería self.Child.Name
o simplemente Child.Name
(en función de cómo se estuviera usando el valor de clave).
Obtención de valores mediante codificación de clave-valor
El método ValueForKey
devuelve el valor de la clave especificada (como NSString
), en relación con la instancia de la clase KVC que recibe la solicitud. Por ejemplo, si Person
es una instancia de la clase PersonModel
definida anteriormente:
// Read value
var name = Person.ValueForKey (new NSString("Name"));
Esto devolvería el valor de la propiedad Name
para esa instancia de PersonModel
.
Establecimiento de valores mediante codificación de clave-valor
Del mismo modo, el SetValueForKey
establece el valor para la clave especificada (como NSString
), relativa a la instancia de la clase KVC que recibe la solicitud. Por lo tanto, de nuevo, con una instancia de la clase PersonModel
, como se muestra a continuación:
// Write value
Person.SetValueForKey(new NSString("Jane Doe"), new NSString("Name"));
Cambiaría el valor de la propiedad Name
a Jane Doe
.
Observación de cambios de valor
Usando la observación clave-valor (KVO), puede adjuntar un observador a una clave específica de una clase compatible con KVC y ser notificado cada vez que se modifique el valor de esa clave (ya sea usando técnicas de KVC o accediendo directamente a la propiedad dada en código C#). Por ejemplo:
// Watch for the name value changing
Person.AddObserver ("Name", NSKeyValueObservingOptions.New, (sender) => {
// Inform caller of selection change
Console.WriteLine("New Name: {0}", Person.Name)
});
Ahora, cada vez que se modifica la propiedad Name
de la instancia Person
de la clase PersonModel
, el nuevo valor se escribe en la consola.
Para más información, consulte la Guía de programación de introducción a la observación de clave-valor de Apple.
Enlace de datos
Las siguientes secciones mostrarán cómo puede usar una clase que cumpla con la codificación clave-valor para enlazar datos a los elementos de la interfaz de usuario en Interface Builder de Xcode, en lugar de leer y escribir valores usando código C#. De este modo, separa su Modelo de datos de las vistas que se usan para mostrarlos, lo que hace que la aplicación Xamarin.Mac sea más flexible y fácil de mantener. También se reduce considerablemente la cantidad de código que se debe escribir.
Definición del modelo de datos
Antes de poder enlazar datos de un elemento de interfaz de usuario en Interface Builder, debe tener una clase compatible con KVC/KVO definida en su aplicación Xamarin.Mac para que actúe como Modelo de datos para el enlace. El Modelo de datos proporciona todos los datos que se mostrarán en la Interfaz de usuario y recibe cualquier modificación de los datos que el usuario realice en la interfaz de usuario mientras ejecuta la aplicación.
Por ejemplo, si estuviera escribiendo una aplicación que administrara un grupo de empleados, podría usar la siguiente clase para definir el Modelo de datos:
using System;
using Foundation;
using AppKit;
namespace MacDatabinding
{
[Register("PersonModel")]
public class PersonModel : NSObject
{
#region Private Variables
private string _name = "";
private string _occupation = "";
private bool _isManager = false;
private NSMutableArray _people = new NSMutableArray();
#endregion
#region Computed Properties
[Export("Name")]
public string Name {
get { return _name; }
set {
WillChangeValue ("Name");
_name = value;
DidChangeValue ("Name");
}
}
[Export("Occupation")]
public string Occupation {
get { return _occupation; }
set {
WillChangeValue ("Occupation");
_occupation = value;
DidChangeValue ("Occupation");
}
}
[Export("isManager")]
public bool isManager {
get { return _isManager; }
set {
WillChangeValue ("isManager");
WillChangeValue ("Icon");
_isManager = value;
DidChangeValue ("isManager");
DidChangeValue ("Icon");
}
}
[Export("isEmployee")]
public bool isEmployee {
get { return (NumberOfEmployees == 0); }
}
[Export("Icon")]
public NSImage Icon {
get {
if (isManager) {
return NSImage.ImageNamed ("group.png");
} else {
return NSImage.ImageNamed ("user.png");
}
}
}
[Export("personModelArray")]
public NSArray People {
get { return _people; }
}
[Export("NumberOfEmployees")]
public nint NumberOfEmployees {
get { return (nint)_people.Count; }
}
#endregion
#region Constructors
public PersonModel ()
{
}
public PersonModel (string name, string occupation)
{
// Initialize
this.Name = name;
this.Occupation = occupation;
}
public PersonModel (string name, string occupation, bool manager)
{
// Initialize
this.Name = name;
this.Occupation = occupation;
this.isManager = manager;
}
#endregion
#region Array Controller Methods
[Export("addObject:")]
public void AddPerson(PersonModel person) {
WillChangeValue ("personModelArray");
isManager = true;
_people.Add (person);
DidChangeValue ("personModelArray");
}
[Export("insertObject:inPersonModelArrayAtIndex:")]
public void InsertPerson(PersonModel person, nint index) {
WillChangeValue ("personModelArray");
_people.Insert (person, index);
DidChangeValue ("personModelArray");
}
[Export("removeObjectFromPersonModelArrayAtIndex:")]
public void RemovePerson(nint index) {
WillChangeValue ("personModelArray");
_people.RemoveObject (index);
DidChangeValue ("personModelArray");
}
[Export("setPersonModelArray:")]
public void SetPeople(NSMutableArray array) {
WillChangeValue ("personModelArray");
_people = array;
DidChangeValue ("personModelArray");
}
#endregion
}
}
La mayoría de las características de esta clase se trataron anteriormente en Qué es la codificación clave-valor. Sin embargo, veamos algunos elementos específicos y algunas adiciones que se hicieron para permitir que esta clase actúe como Modelo de datos para Controladores de matriz y Controladores de árbol (que usaremos más adelante para enlazar datos de Vistas de árbol, Vistas Esquema y Vistas de colección).
En primer lugar, dado que un empleado puede ser administrador, hemos usado un NSArray
(concretamente un NSMutableArray
para que los valores puedan modificarse) para permitir que los empleados que administra estén vinculados a ellos:
private NSMutableArray _people = new NSMutableArray();
...
[Export("personModelArray")]
public NSArray People {
get { return _people; }
}
Dos cosas que debe tener en cuenta aquí:
- Usamos un
NSMutableArray
en lugar de una matriz o colección estándar de C#, ya que es un requisito para enlazar datos a controles de AppKit como Vistas de tabla, Vistas Esquema y Colecciones. - Hemos expuesto la matriz de empleados convirtiéndola en
NSArray
a efectos de enlace de datos y hemos cambiado su nombre con formato C#,People
, por uno que espera el enlace de datos,personModelArray
de la forma {nombre_clase}Matriz (observe que el primer carácter se ha puesto en minúsculas).
A continuación, tenemos que agregar algunos métodos públicos con nombres especiales para que sean compatibles con Controladores de matriz y Controladores de árbol:
[Export("addObject:")]
public void AddPerson(PersonModel person) {
WillChangeValue ("personModelArray");
isManager = true;
_people.Add (person);
DidChangeValue ("personModelArray");
}
[Export("insertObject:inPersonModelArrayAtIndex:")]
public void InsertPerson(PersonModel person, nint index) {
WillChangeValue ("personModelArray");
_people.Insert (person, index);
DidChangeValue ("personModelArray");
}
[Export("removeObjectFromPersonModelArrayAtIndex:")]
public void RemovePerson(nint index) {
WillChangeValue ("personModelArray");
_people.RemoveObject (index);
DidChangeValue ("personModelArray");
}
[Export("setPersonModelArray:")]
public void SetPeople(NSMutableArray array) {
WillChangeValue ("personModelArray");
_people = array;
DidChangeValue ("personModelArray");
}
Estos permiten a los controladores solicitar y modificar los datos que muestran. Al igual que la NSArray
expuesta anteriormente, tienen una convención de nomenclatura muy específica (que difiere de las convenciones de nomenclatura típicas de C#):
addObject:
: agrega un objeto a la matriz.insertObject:in{class_name}ArrayAtIndex:
: donde{class_name}
es el nombre de la clase. Este método inserta un objeto en la matriz en un índice determinado.removeObjectFrom{class_name}ArrayAtIndex:
: donde{class_name}
es el nombre de la clase. Este método quita el objeto de la matriz en un índice determinado.set{class_name}Array:
: donde{class_name}
es el nombre de la clase. Este método permite reemplazar el transporte existente por uno nuevo.
Dentro de estos métodos, hemos encapsulado los cambios en la matriz en mensajes WillChangeValue
y DidChangeValue
para el cumplimiento de la KVO.
Por último, dado que la propiedad Icon
depende del valor de la propiedad isManager
, es posible que los cambios en la propiedad isManager
no se reflejen en el Icon
de los elementos de la interfaz de usuario vinculados a datos (durante la KVO):
[Export("Icon")]
public NSImage Icon {
get {
if (isManager) {
return NSImage.ImageNamed ("group.png");
} else {
return NSImage.ImageNamed ("user.png");
}
}
}
Para corregirlo, usamos el código siguiente:
[Export("isManager")]
public bool isManager {
get { return _isManager; }
set {
WillChangeValue ("isManager");
WillChangeValue ("Icon");
_isManager = value;
DidChangeValue ("isManager");
DidChangeValue ("Icon");
}
}
Observe que además de su propia Clave, el descriptor de acceso isManager
también está enviando los mensajes WillChangeValue
y DidChangeValue
para la Clave Icon
, por lo que también verá el cambio.
Usaremos el Modelo de datos PersonModel
durante el resto de este artículo.
Enlace de datos simple
Con nuestro Modelo de datos definido, veamos un ejemplo sencillo de enlace de datos en Interface Builder de Xcode. Por ejemplo, vamos a agregar un formulario a nuestra aplicación de Xamarin.Mac que puede usarse para editar el PersonModel
que definimos anteriormente. Agregaremos algunos campos de texto y una casilla para mostrar y editar las propiedades del modelo.
En primer lugar, vamos a agregar un nuevo Controlador de vista a nuestro archivo Main.storyboard en Interface Builder y a nombrar su clase SimpleViewController
:
A continuación, vuelva a Visual Studio para Mac, edite el archivo SimpleViewController.cs (que se añadió automáticamente a nuestro proyecto) y exponga una instancia del PersonModel
al que enlazaremos los datos de nuestro formulario. Agregue el siguiente código:
private PersonModel _person = new PersonModel();
...
[Export("Person")]
public PersonModel Person {
get {return _person; }
set {
WillChangeValue ("Person");
_person = value;
DidChangeValue ("Person");
}
}
A continuación, cuando se cargue la Vista, vamos a crear una instancia de nuestro PersonModel
y a rellenarla con este código:
public override void ViewDidLoad ()
{
base.AwakeFromNib ();
// Set a default person
var Craig = new PersonModel ("Craig Dunn", "Documentation Manager");
Craig.AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
Craig.AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
Person = Craig;
}
Ahora tenemos que crear nuestro formulario, haga doble clic en el archivo Main.storyboard para abrirlo y editarlo en Interface Builder. Diseñe el formulario de forma que se parezca a lo siguiente:
Para enlazar los datos del formulario al PersonModel
que expusimos mediante la clave Person
, haga lo siguiente:
Seleccione el campo de texto Nombre del empleado y cambie al Inspector de enlaces.
Marque la casilla Enlazar con y seleccione Controlador de vista simple en la lista desplegable. A continuación, escriba
self.Person.Name
para la Ruta de acceso:Seleccione el campo de texto Ocupación y marque la casilla Enlazar a y seleccione Controlador de vista simple en la lista desplegable. A continuación, escriba
self.Person.Occupation
para la Ruta de acceso de clave:Seleccione la casilla El empleado es un administrador, marque la casilla Enlazar a y seleccione Controlador de vista simple en la lista desplegable. A continuación, escriba
self.Person.isManager
para la Ruta de acceso de clave:Seleccione el campo de texto Número de empleados administrados, marque la casilla Enlazar a y seleccione Controlador de vista simple en la lista desplegable. A continuación, escriba
self.Person.NumberOfEmployees
para la Ruta de acceso de clave:Si el empleado no es administrador, queremos ocultar la etiqueta Número de empleados administrados y el campo de texto.
Seleccione la etiqueta Número de empleados administrados, expanda la lista desplegable Ocultos y marque la casilla Enlazar con y seleccione Controlador de vista simple en el menú desplegable. A continuación, escriba
self.Person.isManager
para la Ruta de acceso de clave:Seleccione
NSNegateBoolean
en la lista desplegable Transformador de valores:Esto indica al enlace de datos que la etiqueta se ocultará si el valor de la propiedad
isManager
esfalse
.Repita los pasos 7 y 8 para el campo de texto Número de empleados administrados.
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Si ejecuta la aplicación, los valores de la propiedad Person
rellenarán automáticamente nuestro formulario:
Cualquier cambio que el usuario realice en el formulario se volverá a escribir en la propiedad Person
del Controlador de vista. Por ejemplo, al desmarcar El empleado es administrador se actualiza la instancia Person
de nuestro PersonModel
y la etiquetaNúmero de empleados administrados y el campo de texto se ocultan automáticamente (mediante el enlace de datos):
Enlace de datos de vista de tabla
Ahora que ya conocemos los conceptos básicos del enlace de datos, veamos una tarea de enlace de datos más compleja usando un Controlador de matriz y enlazando datos a una vista de tabla. Para obtener más información sobre cómo trabajar con vistas de tabla, consulte nuestra documentación de Vistas de tabla.
En primer lugar, agreguemos un nuevo Controlador de vista a nuestro archivo Main.storyboard en Interface Builder y nombremos su clase TableViewController
:
A continuación, editemos el archivo TableViewController.cs (que se añadió automáticamente a nuestro proyecto) y expongamos una matriz (NSArray
) de clases PersonModel
a las que enlazaremos los datos de nuestro formulario. Agregue el siguiente código:
private NSMutableArray _people = new NSMutableArray();
...
[Export("personModelArray")]
public NSArray People {
get { return _people; }
}
...
[Export("addObject:")]
public void AddPerson(PersonModel person) {
WillChangeValue ("personModelArray");
_people.Add (person);
DidChangeValue ("personModelArray");
}
[Export("insertObject:inPersonModelArrayAtIndex:")]
public void InsertPerson(PersonModel person, nint index) {
WillChangeValue ("personModelArray");
_people.Insert (person, index);
DidChangeValue ("personModelArray");
}
[Export("removeObjectFromPersonModelArrayAtIndex:")]
public void RemovePerson(nint index) {
WillChangeValue ("personModelArray");
_people.RemoveObject (index);
DidChangeValue ("personModelArray");
}
[Export("setPersonModelArray:")]
public void SetPeople(NSMutableArray array) {
WillChangeValue ("personModelArray");
_people = array;
DidChangeValue ("personModelArray");
}
Tal y como hicimos en la clase PersonModel
anteriormente en la sección Definición de su Modelo de datos, hemos expuesto cuatro métodos públicos con nombres especiales para que el Controlador de matriz lea y escriba datos de nuestra colección de PersonModels
.
A continuación, cuando se carga la vista, es necesario rellenar la matriz con este código:
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Build list of employees
AddPerson (new PersonModel ("Craig Dunn", "Documentation Manager", true));
AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
AddPerson (new PersonModel ("Larry O'Brien", "API Documentation Manager", true));
AddPerson (new PersonModel ("Mike Norman", "API Documenter"));
}
Ahora tenemos que crear nuestra vista de tabla, haga doble clic en el archivo Main.storyboard para abrirlo y editarlo en Interface Builder. Diseñe la tabla de forma que se parezca a lo siguiente:
Necesitamos agregar un Controlador de matriz para proporcionar datos enlazados a nuestra tabla, haga lo siguiente:
Arrastre un Controlador de matriz del Inspector de biblioteca al Editor de interfaz:
Seleccione Controlador de matriz en la Jerarquía de interfaces y cambie al Inspector de atributos:
Escriba
PersonModel
para el Nombre de la clase, haga clic en el botón Más y agregue tres claves. NómbrelasName
,Occupation
yisManager
:Esto indica al Controlador de matriz de qué está administrando una matriz y qué propiedades debe exponer (mediante claves).
Cambie al Inspector de enlaces y en Matriz de contenido seleccione Enlazar con y Controlador de vista de tabla. Escriba una Ruta de acceso de modelo de
self.personModelArray
:Esto vincula el Controlador de matriz a la matriz de
PersonModels
que expusimos en nuestro Controlador de vista.
Ahora tenemos que enlazar nuestra vista de tabla con el Controlador de matriz, haga lo siguiente:
Seleccione la Vista de tabla y el Inspector de enlaces:
En el menú desplegable Contenido de la tabla, seleccione Enlazar con y Controlador de matriz. Escriba
arrangedObjects
para el campo Clave del controlador:Seleccione la Celda de vista de tabla bajo la columna Empleado. En el Inspector de enlaces, bajo el desplegable Valor, seleccione Enlazar a y Vista de celdas de tabla. Escriba
objectValue.Name
para la Ruta de acceso de la clave de modelo:objectValue
es elPersonModel
actual en la matriz que está siendo administrada por el Controlador de matriz.Seleccione la Celda de vista de tabla bajo la columna Ocupación. En el Inspector de enlaces, bajo el desplegable Valor, seleccione Enlazar a y Vista de celdas de tabla. Escriba
objectValue.Occupation
para la Ruta de acceso de la clave de modelo:Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Si ejecutamos la aplicación, la tabla se rellenará con nuestra matriz de PersonModels
:
Enlace de datos de vista Esquema
El enlace de datos con una Vista Esquema es muy similar al enlace con una Vista de tabla. La diferencia clave es que usaremos un Controlador de árbol en lugar de un Controlador de matriz para proporcionar los datos enlazados a la vista Esquema. Para obtener más información sobre cómo trabajar con vistas de esquema, consulte nuestra documentación de Vistas de esquema.
En primer lugar, agreguemos un nuevo Controlador de vista a nuestro archivo Main.storyboard en Interface Builder y nombremos su clase OutlineViewController
:
A continuación, editemos el archivo OutlineViewController.cs (que se añadió automáticamente a nuestro proyecto) y expongamos una matriz (NSArray
) de clases PersonModel
a las que enlazaremos los datos de nuestro formulario. Agregue el siguiente código:
private NSMutableArray _people = new NSMutableArray();
...
[Export("personModelArray")]
public NSArray People {
get { return _people; }
}
...
[Export("addObject:")]
public void AddPerson(PersonModel person) {
WillChangeValue ("personModelArray");
_people.Add (person);
DidChangeValue ("personModelArray");
}
[Export("insertObject:inPersonModelArrayAtIndex:")]
public void InsertPerson(PersonModel person, nint index) {
WillChangeValue ("personModelArray");
_people.Insert (person, index);
DidChangeValue ("personModelArray");
}
[Export("removeObjectFromPersonModelArrayAtIndex:")]
public void RemovePerson(nint index) {
WillChangeValue ("personModelArray");
_people.RemoveObject (index);
DidChangeValue ("personModelArray");
}
[Export("setPersonModelArray:")]
public void SetPeople(NSMutableArray array) {
WillChangeValue ("personModelArray");
_people = array;
DidChangeValue ("personModelArray");
}
Tal y como hicimos en la clase PersonModel
anteriormente en la sección Definición de su Modelo de datos, hemos expuesto cuatro métodos públicos con nombres especiales para que el Controlador de árbol lea y escriba datos de nuestra colección de PersonModels
.
A continuación, cuando se carga la vista, es necesario rellenar la matriz con este código:
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Build list of employees
var Craig = new PersonModel ("Craig Dunn", "Documentation Manager");
Craig.AddPerson (new PersonModel ("Amy Burns", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Joel Martinez", "Web & Infrastructure"));
Craig.AddPerson (new PersonModel ("Kevin Mullins", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Mark McLemore", "Technical Writer"));
Craig.AddPerson (new PersonModel ("Tom Opgenorth", "Technical Writer"));
AddPerson (Craig);
var Larry = new PersonModel ("Larry O'Brien", "API Documentation Manager");
Larry.AddPerson (new PersonModel ("Mike Norman", "API Documenter"));
AddPerson (Larry);
}
Ahora tenemos que crear nuestra vista Esquema, haga doble clic en el archivo Main.storyboard para abrirlo y editarlo en Interface Builder. Diseñe la tabla de forma que se parezca a lo siguiente:
Necesitamos agregar un Controlador de árbol para proporcionar datos enlazados a nuestro esquema, haga lo siguiente:
Arrastre un Controlador de árbol desde el Inspector de biblioteca al Editor de interfaz:
Seleccione Controlador de árbol en la Jerarquía de interfaces y cambie al Inspector de atributos:
Escriba
PersonModel
para el Nombre de la clase, haga clic en el botón Más y agregue tres claves. NómbrelasName
,Occupation
yisManager
:Esto indica al Controlador de árbol de qué está administrando una matriz y qué propiedades debe exponer (mediante claves).
En la sección Controlador de árbol, escriba
personModelArray
para Elementos secundarios, escribaNumberOfEmployees
en Recuento y escribaisEmployee
en Hoja:Indica al Controlador del árbol dónde encontrar los nodos secundarios, cuántos hay y si el nodo actual tiene nodos secundarios.
Cambie al Inspector de enlaces y en Matriz de contenido seleccione Enlazar con y Propietario del archivo. Escriba una Ruta de acceso de modelo de
self.personModelArray
:Esto vincula el Controlador de árbol a la matriz de
PersonModels
que expusimos en nuestro Controlador de vista.
Ahora tenemos que enlazar nuestra vista Esquema con el Controlador de árbol, haga lo siguiente:
Seleccione la vista Esquema y en el Inspector de enlaces seleccione :
Bajo el menú desplegable Contenido de vista Esquema, seleccione Enlazar a y Controlador de árbol. Escriba
arrangedObjects
para el campo Clave del controlador:Seleccione la Celda de vista de tabla bajo la columna Empleado. En el Inspector de enlaces, bajo el desplegable Valor, seleccione Enlazar a y Vista de celdas de tabla. Escriba
objectValue.Name
para la Ruta de acceso de la clave de modelo:objectValue
es elPersonModel
actual en la matriz que está siendo administrada por el Controlador de árbol.Seleccione la Celda de vista de tabla bajo la columna Ocupación. En el Inspector de enlaces, bajo el desplegable Valor, seleccione Enlazar a y Vista de celdas de tabla. Escriba
objectValue.Occupation
para la Ruta de acceso de la clave de modelo:Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Si ejecutamos la aplicación, el esquema se rellenará con nuestra matriz de PersonModels
:
Enlace de datos de la vista de colección
El enlace de datos con una vista de colección es muy similar al enlace con una vista de tabla, ya que se usa un Controlador de matriz para proporcionar datos para la colección. Dado que la vista de colección no tiene un formato de presentación preestablecido, se requiere más trabajo para proporcionar comentarios de interacción del usuario y realizar un seguimiento de la selección del usuario.
Importante
Debido a un problema en Xcode 7 y macOS 10.11 (y superiores), las vistas de colección no pueden usarse dentro de un guion gráfico (.storyboard). Como resultado, tendrá que seguir usando archivos .xib para definir las vistas de colección de sus aplicaciones de Xamarin.Mac. Consulte nuestra documentación de Vistas de colección para obtener más información.
Depuración de bloqueos nativos
Cometer un error en sus enlaces de datos puede provocar un bloqueo nativo en código no administrado y hacer que su aplicación de Xamarin.Mac falle por completo con un error SIGABRT
:
Suele haber cuatro causas principales de bloqueos nativos durante el enlace de datos:
- Su Modelo de datos no hereda de
NSObject
ni es una subclase deNSObject
. - No ha expuesto su propiedad a Objective-C usando el atributo
[Export("key-name")]
. - No ha encapsulado los cambios en el valor del descriptor de acceso en las llamadas a los métodos
WillChangeValue
yDidChangeValue
(especificando la misma clave que el atributoExport
). - Tiene una clave incorrecta o mal escrita en el Inspector de enlaces en Interface Builder.
Descodificación de un bloqueo
Provoquemos un bloqueo nativo en nuestro enlace de datos para poder mostrar cómo localizarlo y solucionarlo. En Interface Builder, vamos a cambiar nuestro enlace de la primera etiqueta en el ejemplo de la vista de colección de Name
a Title
:
Guardemos el cambio, volvamos a Visual Studio para Mac para sincronizarlo con Xcode y ejecutemos nuestra aplicación. Cuando se muestre la vista de colección, la aplicación se bloqueará momentáneamente con un error SIGABRT
(como se muestra en la Salida de la aplicación en Visual Studio para Mac) ya que el PersonModel
no expone una propiedad con la clave Title
:
Si nos desplazamos hasta la parte superior del error en Salida de la aplicación podremos ver la clave para resolver este problema:
Esta línea nos está diciendo que la clave Title
no existe en el objeto al que estamos enlazando. Si volvemos a cambiar el enlace en Name
en Interface Builder, guardamos, sincronizamos, recompilamos y ejecutamos, la aplicación se ejecutará como se esperaba sin ningún problema.
Resumen
En este artículo, se revisó en detalle el trabajo con el enlace de datos y la codificación de clave-valor en una aplicación de Xamarin.Mac. Primero, se ha examinado la exposición de una clase de C# a Objective-C mediante la codificación de clave-valor (KVC) y la observación de clave-valor (KVO). Luego, se ha mostrado cómo usar una clase compatible con KVO y enlazar datos a elementos de interfaz de usuario en Interface Builder de Xcode. Por último, ha mostrado el enlace de datos complejos usando Controladores de matriz y Controladores de árbol.
Vínculos relacionados
- Hello, Mac
- Controles estándar
- Vistas de tabla
- Vistas de esquema
- Vistas de colecciones
- Guía de programación de codificación de clave-valor
- Introducción a la guía de programación de observación de clave-valor
- Introducción a los temas de programación de enlaces de Cocoa
- Introducción a la referencia de enlaces de Cocoa
- NSCollectionView
- Directrices de la interfaz humana de macOS