Data binding e codifica chiave-valore in Xamarin.Mac

Questo articolo illustra l'uso della codifica chiave-valore e dell'osservazione chiave-valore per consentire il data binding agli elementi dell'interfaccia utente in Interface Builder di Xcode.

Panoramica

Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere alle stesse tecniche di codifica chiave-valore e di data binding utilizzate da uno sviluppatore in Objective-C e Xcode . Poiché Xamarin.Mac si integra direttamente con Xcode, è possibile usare Interface Builder di Xcode per Data Bind con elementi dell'interfaccia utente invece di scrivere codice.

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

An example of the running app

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

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

Che cos'è la codifica chiave-valore

La codifica chiave-valore (KVC) è un meccanismo per accedere indirettamente alle proprietà di un oggetto, usando chiavi (stringhe formattate appositamente) per identificare le proprietà anziché accedervi tramite variabili di istanza o metodi di accesso (get/set). Implementando funzioni di accesso conformi alla codifica chiave-valore nell'applicazione Xamarin.Mac, si ottiene l'accesso ad altre funzionalità macOS (in precedenza note come OS X), come l'osservazione chiave-valore (KVO), il data binding, Core Data, le associazioni cocoa e la scriptability.

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

Si esaminerà ad esempio la definizione di classe seguente di un oggetto conforme a 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 ()
        {
        }
    }
}

Prima di tutto, l'attributo [Register("PersonModel")] registra la classe e la espone a Objective-C. Quindi, la classe deve ereditare da NSObject (o una sottoclasse che eredita da NSObject), questo aggiunge diversi metodi di base che consentono alla classe di essere conformi a KVC. Successivamente, l'attributo [Export("Name")] espone la Name proprietà e definisce il valore Key che verrà usato successivamente per accedere alla proprietà tramite le tecniche KVC e KVO.

Infine, per essere in grado di essere Key-Value Observed modifiche apportate al valore della proprietà, la funzione di accesso deve eseguire il wrapping delle modifiche apportate al relativo valore nelle WillChangeValue chiamate al metodo e DidChangeValue specificando la stessa chiave dell'attributo Export . Ad esempio:

set {
    WillChangeValue ("Name");
    _name = value;
    DidChangeValue ("Name");
}

Questo passaggio è molto importante per il data binding in Interface Builder di Xcode(come vedremo più avanti in questo articolo).

Per altre informazioni, vedere la Guida alla programmazione della codifica chiave-valore di Apple.

Chiavi e percorsi chiave

Una chiave è una stringa che identifica una proprietà specifica di un oggetto . In genere, una chiave corrisponde al nome di un metodo della funzione di accesso in un oggetto conforme alla codifica chiave-valore. Le chiavi devono usare la codifica ASCII, in genere iniziano con una lettera minuscola e potrebbero non contenere spazi vuoti. Quindi, dato l'esempio precedente, Name sarebbe un valore chiave della Name proprietà della PersonModel classe . La chiave e il nome della proprietà che espongono non devono essere uguali, ma nella maggior parte dei casi sono.

Un percorso chiave è una stringa di chiavi separate da punti usati per specificare una gerarchia di proprietà dell'oggetto da attraversare. La proprietà della prima chiave nella sequenza è relativa al ricevitore e ogni chiave successiva viene valutata rispetto al valore della proprietà precedente. Allo stesso modo si usa la notazione punto per attraversare un oggetto e le relative proprietà in una classe C#.

Ad esempio, se è stata espansa la PersonModel classe e la proprietà aggiunta 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 ()
        {
        }
    }
}

Il percorso chiave del nome dell'elemento figlio sarebbe self.Child.Name o semplicemente Child.Name (in base alla modalità di utilizzo del valore chiave).

Recupero di valori tramite la codifica chiave-valore

Il ValueForKey metodo restituisce il valore per la chiave specificata (come ), NSStringrispetto all'istanza della classe KVC che riceve la richiesta. Ad esempio, se Person è un'istanza della PersonModel classe definita in precedenza:

// Read value
var name = Person.ValueForKey (new NSString("Name"));

Verrà restituito il valore della proprietà per l'istanza Name di PersonModel.

Impostazione dei valori tramite la codifica chiave-valore

Analogamente, SetValueForKey impostare il valore per la chiave specificata (come ), NSStringrispetto all'istanza della classe KVC che riceve la richiesta. Quindi, anche in questo caso, usando un'istanza della PersonModel classe , come illustrato di seguito:

// Write value
Person.SetValueForKey(new NSString("Jane Doe"), new NSString("Name"));

Modificare il valore della Name proprietà in Jane Doe.

Osservazione delle modifiche dei valori

Usando l'osservazione chiave-valore (KVO), è possibile associare un osservatore a una chiave specifica di una classe conforme a KVC e ricevere una notifica ogni volta che il valore per tale chiave viene modificato (usando tecniche KVC o accedendo direttamente alla proprietà specificata nel codice C#). Ad esempio:

// Watch for the name value changing
Person.AddObserver ("Name", NSKeyValueObservingOptions.New, (sender) => {
    // Inform caller of selection change
    Console.WriteLine("New Name: {0}", Person.Name)
});

Ora, ogni volta che la Name proprietà dell'istanza Person della PersonModel classe viene modificata, il nuovo valore viene scritto nella console.

Per altre informazioni, vedere Introduzione a Key-Value Observing Programming Guide (Introduzione a Key-Value Observing Programming Guide) di Apple.

Data binding

Le sezioni seguenti illustrano come usare una classe conforme a codice chiave-valore e chiave-valore per associare i dati agli elementi dell'interfaccia utente in Interface Builder di Xcode, invece di leggere e scrivere valori usando il codice C#. In questo modo si separa il modello di dati dalle viste usate per visualizzarle, rendendo l'applicazione Xamarin.Mac più flessibile e più semplice da gestire. Si riduce notevolmente anche la quantità di codice che deve essere scritta.

Definizione del modello di dati

Prima di poter associare dati a un elemento dell'interfaccia utente in Interface Builder, è necessario avere una classe conforme a KVC/KVO definita nell'applicazione Xamarin.Mac per fungere da modello di dati per l'associazione. Il modello di dati fornisce tutti i dati che verranno visualizzati nell'interfaccia utente e riceve tutte le modifiche ai dati apportati dall'utente nell'interfaccia utente durante l'esecuzione dell'applicazione.

Ad esempio, se si scrive un'applicazione che ha gestito un gruppo di dipendenti, è possibile usare la classe seguente per definire il modello di dati:

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 maggior parte delle funzionalità di questa classe è stata illustrata nella sezione Che cos'è la codifica chiave-valore precedente. Verranno tuttavia esaminati alcuni elementi specifici e alcune aggiunte che sono state apportate per consentire a questa classe di fungere da modello di dati per controller di matrice e controller dell'albero, che verranno usati in un secondo momento per associare le viste albero, le visualizzazioni struttura e le visualizzazioni raccolta.

In primo luogo, poiché un dipendente potrebbe essere un manager, è stato usato un NSArray oggetto (in particolare, NSMutableArray in modo che i valori possano essere modificati) per consentire ai dipendenti che sono riusciti ad essere collegati a loro:

private NSMutableArray _people = new NSMutableArray();
...

[Export("personModelArray")]
public NSArray People {
    get { return _people; }
}

Due aspetti da notare qui:

  1. È stata usata una NSMutableArray matrice o una raccolta C# standard perché si tratta di un requisito per l'associazione di dati ai controlli AppKit, ad esempio visualizzazioni tabella, visualizzazioni struttura e raccolte.
  2. È stata esposta la matrice di dipendenti eseguendone il cast a per NSArray scopi di data binding e modificandone il nome formattato C#, People, in uno previsto dal data binding, personModelArray nel formato {class_name}Array (si noti che il primo carattere è stato fatto in minuscolo).

Successivamente, è necessario aggiungere alcuni metodi pubblici con nomi specifici per supportare i controller array e i controller albero:

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

Questi consentono ai titolari del trattamento di richiedere e modificare i dati visualizzati. Come illustrato in precedenza NSArray , questi hanno una convenzione di denominazione molto specifica (diversa dalle convenzioni di denominazione C# tipiche):

  • addObject: - Aggiunge un oggetto alla matrice.
  • insertObject:in{class_name}ArrayAtIndex: - Dove {class_name} è il nome della classe. Questo metodo inserisce un oggetto nella matrice in corrispondenza di un determinato indice.
  • removeObjectFrom{class_name}ArrayAtIndex: - Dove {class_name} è il nome della classe. Questo metodo rimuove l'oggetto nella matrice in corrispondenza di un determinato indice.
  • set{class_name}Array: - Dove {class_name} è il nome della classe. Questo metodo consente di sostituire il trasporto esistente con uno nuovo.

All'interno di questi metodi è stato eseguito il wrapping delle modifiche apportate alla matrice in WillChangeValue e DidChangeValue ai messaggi per la conformità KVO.

Infine, poiché la Icon proprietà si basa sul valore della isManager proprietà , le modifiche apportate alla isManager proprietà potrebbero non essere riflesse negli elementi dell'interfaccia Icon utente associati a dati (durante KVO):

[Export("Icon")]
public NSImage Icon {
    get {
        if (isManager) {
            return NSImage.ImageNamed ("group.png");
        } else {
            return NSImage.ImageNamed ("user.png");
        }
    }
}

Per correggere questo errore, viene usato il codice seguente:

[Export("isManager")]
public bool isManager {
    get { return _isManager; }
    set {
        WillChangeValue ("isManager");
        WillChangeValue ("Icon");
        _isManager = value;
        DidChangeValue ("isManager");
        DidChangeValue ("Icon");
    }
}

Si noti che oltre alla propria chiave, la isManager funzione di accesso invia anche i WillChangeValue messaggi e DidChangeValue per la Icon chiave in modo che visualizzi anche la modifica.

Nel resto di questo articolo verrà usato il PersonModel modello di dati.

Data binding semplice

Dopo aver definito il modello di dati, si esaminerà un semplice esempio di data binding in Interface Builder di Xcode. Ad esempio, aggiungere un modulo all'applicazione Xamarin.Mac che può essere usata per modificare l'oggetto PersonModel definito in precedenza. Verranno aggiunti alcuni campi di testo e una casella di controllo per visualizzare e modificare le proprietà del modello.

Aggiungere prima di tutto un nuovo controller di visualizzazione al file Main.storyboard in Interface Builder e denominare la classe SimpleViewController:

Adding a new view controller with a class named SimpleViewController.

Tornare quindi a Visual Studio per Mac, modificare il file di SimpleViewController.cs (aggiunto automaticamente al progetto) ed esporre un'istanza PersonModel di a cui verrà eseguito il data binding del modulo. Aggiungere il codice seguente:

private PersonModel _person = new PersonModel();
...

[Export("Person")]
public PersonModel Person {
    get {return _person; }
    set {
        WillChangeValue ("Person");
        _person = value;
        DidChangeValue ("Person");
    }
}

Successivamente, quando viene caricata la visualizzazione, creare un'istanza di PersonModel e popolarla con questo codice:

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;

}

A questo punto è necessario creare il modulo, fare doppio clic sul file Main.storyboard per aprirlo per la modifica in Interface Builder. Layout del modulo in modo che sia simile al seguente:

Editing the storyboard in Xcode

Per associare il modulo all'oggetto PersonModel esposto tramite la Person chiave, eseguire le operazioni seguenti:

  1. Selezionare il campo Testo nome dipendente e passare a Controllo associazioni.

  2. Selezionare la casella Associa a e selezionare Simple View Controller (Controller visualizzazione semplice) nell'elenco a discesa. self.Person.Name Immettere quindi per Il percorso chiave:

    Entering self dot person dot name for the Key Path.

  3. Selezionare il campo Testo occupazione e selezionare la casella Associa a e selezionare Simple View Controller (Controller visualizzazione semplice) nell'elenco a discesa. self.Person.Occupation Immettere quindi per Il percorso chiave:

    Entering self dot Person dot Occupation for the Key Path.

  4. Selezionare la casella di controllo Employee is a Manager (Dipendente è manager) e selezionare Bind to ( Associa a ) e selezionare Simple View Controller (Controller visualizzazione semplice) nell'elenco a discesa. self.Person.isManager Immettere quindi per Il percorso chiave:

    Entering self dot Person dot isManager for the Key Path.

  5. Selezionare il campo Number of Employees Managed Text (Numero di dipendenti gestiti ) e selezionare Bind to (Associa a ) e selezionare Simple View Controller (Controller visualizzazione semplice) nell'elenco a discesa. self.Person.NumberOfEmployees Immettere quindi per Il percorso chiave:

    Entering self dot Person dot NumberOfEmployees for the Key Path.

  6. Se il dipendente non è un responsabile, si vuole nascondere il campo Numero di dipendenti gestiti e testo.

  7. Selezionare l'etichetta gestita Numero di dipendenti, espandere il menu a discesa Nascosto e selezionare Associa a e selezionare Simple View Controller (Controller visualizzazione semplice) dall'elenco a discesa. self.Person.isManager Immettere quindi per Il percorso chiave:

    Entering self dot Person dot isManager for the Key Path for non-managers.

  8. Selezionare dall'elenco a discesa Value Transformer:Select NSNegateBoolean from the Value Transformer dropdown:

    Selecting the NSNegateBoolean key transformation

  9. Indica al data binding che l'etichetta verrà nascosta se il valore della isManager proprietà è false.

  10. Ripetere i passaggi 7 e 8 per il campo Numero di dipendenti gestiti testo.

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

Se si esegue l'applicazione, i valori della proprietà popolano automaticamente il Person modulo:

Showing an auto-populated form

Tutte le modifiche apportate dagli utenti al modulo verranno riscritto nella Person proprietà nel controller di visualizzazione. Ad esempio, deselezionare Employee è un manager che aggiorna l'istanza Person di PersonModel e il campo di testo e l'etichetta gestita Numero di dipendenti vengono nascosti automaticamente (tramite il data binding):

Hiding the number of employees for non-managers

Associazione dati vista tabella

Ora che sono disponibili le nozioni di base del data binding, si esaminerà un'attività di data binding più complessa usando un controller di matrice e il data binding a una vista tabella. Per altre informazioni sull'uso delle viste tabella, vedere la documentazione delle viste tabella.

Aggiungere prima di tutto un nuovo controller di visualizzazione al file Main.storyboard in Interface Builder e denominare la classe TableViewController:

Adding a new view controller with a class named TableViewController.

Successivamente, modificare il file TableViewController.cs (che è stato aggiunto automaticamente al progetto) ed esporre una matrice (NSArray) di PersonModel classi a cui verrà eseguito il data binding del modulo. Aggiungere il codice seguente:

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

Proprio come nella PersonModel classe precedente nella sezione Definizione del modello di dati, sono stati esposti quattro metodi pubblici denominati appositamente in modo che il controller array e legga e scriva i dati dalla raccolta di PersonModels.

Al termine del caricamento della visualizzazione, è necessario popolare la matrice con questo codice:

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

}

A questo punto è necessario creare la visualizzazione tabella, fare doppio clic sul file Main.storyboard per aprirlo per la modifica in Interface Builder. Layout della tabella in modo che sia simile al seguente:

Laying out a new table view

È necessario aggiungere un controller di matrice per fornire i dati associati alla tabella, eseguire le operazioni seguenti:

  1. Trascinare un controller di matrice da Controllo libreria nell'editor dell'interfaccia:

    Selecting an Array Controller from the Library

  2. Selezionare Controller matrice nella gerarchia dell'interfaccia e passare a Controllo attributi:

    Selecting the Attributes Inspector

  3. Immettere PersonModel per Nome classe, fare clic sul pulsante Plus e aggiungere tre chiavi. Denominarli Namee OccupationisManager:

    Adding the required key paths to the Object Controller.

  4. In questo modo il controller array indica di cosa gestisce una matrice di e di quali proprietà deve esporre (tramite chiavi).

  5. Passare a Binding Inspector (Controllo associazioni ) e in Content Array (Matrice di contenuto) selezionare Bind to and Table View Controller (Associa a e controller visualizzazione tabella). Immettere un percorso chiave modello di self.personModelArray:

    Entering a key path

  6. Questo collega il controller di matrice alla matrice di PersonModels che è stata esposta nel controller di visualizzazione.

A questo scopo, è necessario associare la visualizzazione tabella al controller di matrice, eseguire le operazioni seguenti:

  1. Selezionare la visualizzazione Tabella e il controllo binding:

    Selecting the Table View and Binding Inspector.

  2. Nell'elenco a discesa Sommario selezionare Associa a e Controller di matrice. Immettere arrangedObjects per il campo Controller Key (Chiave controller):

    Defining the controller key

  3. Selezionare la cella Visualizzazione tabella nella colonna Employee . In Binding Inspector (Controllo associazioni) sotto l'opzione Value (Valore) selezionare Bind to (Associa a) e Table Cell View (Visualizza cella tabella). Immettere objectValue.Name per model key path (Percorso chiave modello):

    Setting the model key path for the Employee column.

  4. objectValue è l'oggetto corrente PersonModel nella matrice gestita dal controller di matrice.

  5. Selezionare la cella Visualizzazione tabella nella colonna Occupazione . In Binding Inspector (Controllo associazioni) sotto l'opzione Value (Valore) selezionare Bind to (Associa a) e Table Cell View (Visualizza cella tabella). Immettere objectValue.Occupation per model key path (Percorso chiave modello):

    Setting the model key path for the Occupation column.

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

Se si esegue l'applicazione, la tabella verrà popolata con la matrice di PersonModels:

Running the application, which populates the array of PersonModels.

Associazione dati visualizzazione struttura

il data binding rispetto a una visualizzazione struttura è molto simile all'associazione a una vista tabella. La differenza principale è che si userà un controller albero anziché un controller di matrice per fornire i dati associati alla visualizzazione struttura. Per altre informazioni sull'uso delle visualizzazioni struttura, vedere la documentazione sulle visualizzazioni struttura.

Aggiungere prima di tutto un nuovo controller di visualizzazione al file Main.storyboard in Interface Builder e denominare la classe OutlineViewController:

Adding a new view controller with a class named OutlineViewController.

Successivamente, modificare il file OutlineViewController.cs (che è stato aggiunto automaticamente al progetto) ed esporre una matrice (NSArray) di PersonModel classi a cui verrà eseguito il data binding del modulo. Aggiungere il codice seguente:

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

Proprio come è stato fatto nella PersonModel classe precedente nella sezione Definizione del modello di dati, sono stati esposti quattro metodi pubblici denominati appositamente in modo che tree controller e leggere e scrivere dati dalla raccolta di PersonModels.

Al termine del caricamento della visualizzazione, è necessario popolare la matrice con questo codice:

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

}

A questo punto è necessario creare la visualizzazione struttura, fare doppio clic sul file Main.storyboard per aprirlo per la modifica in Interface Builder. Layout della tabella in modo che sia simile al seguente:

Creating the outline view

È necessario aggiungere un controller albero per fornire i dati associati alla struttura, eseguire le operazioni seguenti:

  1. Trascinare un controller albero da Controllo libreria nell'editor dell'interfaccia:

    Selecting a Tree Controller from the Library

  2. Selezionare Tree Controller (Controller albero) nella gerarchia dell'interfaccia e passare a Attribute Inspector (Controllo attributi):

    Selecting the Attribute Inspector

  3. Immettere PersonModel per Nome classe, fare clic sul pulsante Plus e aggiungere tre chiavi. Denominarli Namee OccupationisManager:

    Adding the required key paths for PersonModel.

  4. In questo modo, il controller albero indica di cosa gestisce una matrice di e di quali proprietà deve esporre (tramite chiavi).

  5. Nella sezione Controller albero immettere personModelArray per Children (Figli), immettere NumberOfEmployees in Count (Conteggio) e immettere isEmployee in Leaf (Foglia):

    Setting the Tree Controller key paths

  6. In questo modo viene indicato al controller albero dove trovare i nodi figlio, il numero di nodi figlio presenti e se il nodo corrente ha nodi figlio.

  7. Passare a Controllo associazioni e in Matrice di contenuto selezionare Associa a e Proprietario del file. Immettere un percorso chiave modello di self.personModelArray:

    Editing the key path

  8. Questo collega il controller albero alla matrice di PersonModels che abbiamo esposto nel controller di visualizzazione.

A questo scopo, è necessario associare la visualizzazione Struttura al controller dell'albero, eseguire le operazioni seguenti:

  1. Selezionare la visualizzazione Struttura e in Controllo associazione selezionare :

    Selecting the Outline View and Binding Inspector.

  2. Nell'elenco a discesa Contenuto visualizzazione struttura selezionare Associa a e Controller albero. Immettere arrangedObjects per il campo Controller Key (Chiave controller):

    Setting the controller key

  3. Selezionare la cella Visualizzazione tabella nella colonna Employee . In Binding Inspector (Controllo associazioni) sotto l'opzione Value (Valore) selezionare Bind to (Associa a) e Table Cell View (Visualizza cella tabella). Immettere objectValue.Name per model key path (Percorso chiave modello):

    Entering the model key path value objectValue dot Name.

  4. objectValue è l'oggetto corrente PersonModel nella matrice gestita dal controller albero.

  5. Selezionare la cella Visualizzazione tabella nella colonna Occupazione . In Binding Inspector (Controllo associazioni) sotto l'opzione Value (Valore) selezionare Bind to (Associa a) e Table Cell View (Visualizza cella tabella). Immettere objectValue.Occupation per model key path (Percorso chiave modello):

    Entering the model key path value objectValue dot Occupation.

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

Se si esegue l'applicazione, la struttura verrà popolata con la matrice di PersonModels:

Running the application, which populates our array of PersonModels.

Data binding di visualizzazione raccolta

Il data binding con una visualizzazione raccolta è molto simile all'associazione con una vista tabella, perché un controller di matrice viene usato per fornire dati per la raccolta. Poiché la visualizzazione raccolta non ha un formato di visualizzazione predefinito, è necessario più lavoro per fornire commenti e suggerimenti sull'interazione dell'utente e tenere traccia della selezione dell'utente.

Importante

A causa di un problema in Xcode 7 e macOS 10.11 (e versioni successive), le visualizzazioni della raccolta non possono essere usate all'interno di un file Storyboard (con estensione storyboard). Di conseguenza, sarà necessario continuare a usare i file con estensione xib per definire le visualizzazioni della raccolta per le app Xamarin.Mac. Per altre informazioni, vedere la documentazione delle visualizzazioni raccolte.

Debug di arresti anomali nativi

Se si commette un errore nei data binding, è possibile che si verifica un arresto anomalo nativo nel codice non gestito e che l'applicazione Xamarin.Mac non riesca completamente con un SIGABRT errore:

Example of a native crash dialog box

Durante il data binding sono in genere presenti quattro cause principali di arresti anomali nativi:

  1. Il modello di dati non eredita da NSObject o da una sottoclasse di NSObject.
  2. Non è stata esposta la proprietà all'uso Objective-C dell'attributo [Export("key-name")] .
  3. Non sono state apportate modifiche al valore della funzione di accesso nelle WillChangeValue chiamate al metodo e DidChangeValue , specificando la stessa chiave dell'attributo Export .
  4. Si dispone di una chiave errata o digitata in Binding Inspector in Interface Builder.

Decodifica di un arresto anomalo

Si verificherà un arresto anomalo nativo nel data binding per illustrare come individuarlo e risolverlo. In Interface Builder modificare il binding della prima etichetta nell'esempio di visualizzazione raccolta da Name a Title:

Editing the binding key

Salvare la modifica, tornare a Visual Studio per Mac per eseguire la sincronizzazione con Xcode ed eseguire l'applicazione. Quando viene visualizzata la visualizzazione Raccolta, l'applicazione si arresta momentaneamente in modo anomalo con un SIGABRT errore (come illustrato nell'output dell'applicazione in Visual Studio per Mac) perché PersonModel non espone una proprietà con la chiave Title:

Example of a binding error

Se si scorre fino alla parte superiore dell'errore nell'output dell'applicazione, è possibile visualizzare la chiave per risolvere il problema:

Finding the issue in the error log

Questa riga indica che la chiave Title non esiste nell'oggetto a cui si sta eseguendo l'associazione. Se si modifica nuovamente l'associazione Name in Interface Builder, salvare, sincronizzare, ricompilare ed eseguire, l'applicazione verrà eseguita come previsto senza problemi.

Riepilogo

Questo articolo ha esaminato in dettaglio l'uso del data binding e della codifica chiave-valore in un'applicazione Xamarin.Mac. In primo luogo, ha esaminato l'esposizione di una classe C# a Objective-C usando la codifica chiave-valore (KVC) e l'osservazione chiave-valore (KVO). Successivamente, ha illustrato come usare una classe conforme KVO e Data Bind it agli elementi dell'interfaccia utente in Interface Builder di Xcode. Infine, ha mostrato un data binding complesso usando controller di matrice e controller albero.