Datenbindung und Schlüsselwertcodierung in Xamarin.Mac

In diesem Artikel wird die Verwendung von Key-Value-Codier- und Key-Value-Observing behandelt, um die Datenbindung an UI-Elemente im Benutzeroberflächen-Generator von Xcode zu ermöglichen.

Übersicht

Beim Arbeiten mit C# und .NET in einer Xamarin.Mac-Anwendung haben Sie Zugriff auf die gleichen Schlüsselwertcodierungs- und Datenbindungstechniken, die ein Entwickler ausführt Objective-C und Xcode ausführt. Da Xamarin.Mac direkt in Xcode integriert ist, können Sie den Schnittstellen-Generator von Xcode zum Binden von Daten mit UI-Elementen verwenden, anstatt Code zu schreiben.

Mithilfe von Schlüsselwertcodierungs- und Datenbindungstechniken in Ihrer Xamarin.Mac-Anwendung können Sie die Menge an Code, den Sie schreiben und Standard, um UI-Elemente aufzufüllen und zu bearbeiten, erheblich verringern. Außerdem profitieren Sie von der weiteren Entkoppelung Ihrer Sicherungsdaten (Datenmodell) von der Front-End-Benutzeroberfläche (Model-View-Controller), was zu einfacheren Standard nachhaltigen, flexibleren Anwendungsdesigns führt.

An example of the running app

In diesem Artikel werden die Grundlagen der Arbeit mit Key-Value-Codierung und Datenbindung in einer Xamarin.Mac-Anwendung behandelt. Es wird dringend empfohlen, dass Sie zuerst den Artikel "Hello, Mac " durcharbeiten, insbesondere die Abschnitte "Einführung in Xcode" und "Interface Builder " und "Outlets" und "Actions ", da es sich um wichtige Konzepte und Techniken handelt, die wir in diesem Artikel verwenden werden.

Möglicherweise möchten Sie sich auch die Exposing C#-Klassen /-Methoden im Objective-C Abschnitt des Xamarin.Mac Internals-Dokuments ansehen. Außerdem werden die Register C#-Klassen und Export -Attribute erläutert, die zum Verbinden Ihrer C#-Klassen mit Objective-C Objekten und UI-Elementen verwendet werden.

Was ist key-value coding?

Key-Value Coding (KVC) ist ein Mechanismus für den indirekten Zugriff auf die Eigenschaften eines Objekts mithilfe von Schlüsseln (speziell formatierte Zeichenfolgen), um Eigenschaften zu identifizieren, anstatt über Instanzvariablen oder Accessormethoden (get/set) darauf zuzugreifen. Durch die Implementierung von schlüsselwertkonformen Accessoren in Ihrer Xamarin.Mac-Anwendung erhalten Sie Zugriff auf andere macOS-Features (früher als OS X bezeichnet), z. B. Key-Value Observing (KVO), Datenbindung, Core Data, Cocoa-Bindungen und Skriptierbarkeit.

Mithilfe von Schlüsselwertcodierungs- und Datenbindungstechniken in Ihrer Xamarin.Mac-Anwendung können Sie die Menge an Code, den Sie schreiben und Standard, um UI-Elemente aufzufüllen und zu bearbeiten, erheblich verringern. Außerdem profitieren Sie von der weiteren Entkoppelung Ihrer Sicherungsdaten (Datenmodell) von der Front-End-Benutzeroberfläche (Model-View-Controller), was zu einfacheren Standard nachhaltigen, flexibleren Anwendungsdesigns führt.

Sehen wir uns beispielsweise die folgende Klassendefinition eines KVC-kompatiblen Objekts an:

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

Zunächst registriert das [Register("PersonModel")] Attribut die Klasse und macht sie Objective-Cverfügbar. Anschließend muss die Klasse von NSObject (oder einer Unterklasse, von der geerbt wird) erben NSObject. Dadurch werden mehrere Basismethoden hinzugefügt, mit denen die Klasse KVC-kompatibel sein kann. Als Nächstes macht das [Export("Name")] Attribut die Name Eigenschaft verfügbar und definiert den Key-Wert, der später für den Zugriff auf die Eigenschaft über KVC- und KVO-Techniken verwendet wird.

Schließlich muss der Accessor Änderungen an dem Wert der Eigenschaft umschließen können, um key-Value-Beobachtete Änderungen am Wert der Eigenschaft zu sein, und WillChangeValueDidChangeValue Methodenaufrufe (angeben denselben Schlüssel wie das Export Attribut). Zum Beispiel:

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

Dieser Schritt ist sehr wichtig für die Datenbindung im Schnittstellen-Generator von Xcode (wie wir weiter unten in diesem Artikel sehen).

Weitere Informationen finden Sie im Key-Value Coding-Programmierhandbuch von Apple.

Schlüssel und Schlüsselpfade

Ein Schlüssel ist eine Zeichenfolge, die eine bestimmte Eigenschaft eines Objekts identifiziert. In der Regel entspricht ein Schlüssel dem Namen einer Accessormethode in einem codierungskonformen Schlüsselwertobjekt. Schlüssel müssen ASCII-Codierung verwenden, beginnen normalerweise mit einem Kleinbuchstaben und dürfen keine Leerzeichen enthalten. In Anbetracht des obigen Name Beispiels wäre also ein Key Value-Wert der Name Eigenschaft der PersonModel Klasse. Der Schlüssel und der Name der verfügbaren Eigenschaft müssen nicht identisch sein, in den meisten Fällen sind sie jedoch.

Ein Schlüsselpfad ist eine Zeichenfolge mit punkttrennten Schlüsseln, die verwendet werden, um eine Hierarchie von Objekteigenschaften anzugeben, die durchlaufen werden sollen. Die Eigenschaft des ersten Schlüssels in der Sequenz ist relativ zum Empfänger, und jeder nachfolgende Schlüssel wird relativ zum Wert der vorherigen Eigenschaft ausgewertet. Auf die gleiche Weise verwenden Sie die Punktnotation, um ein Objekt und dessen Eigenschaften in einer C#-Klasse zu durchlaufen.

Beispiel: Wenn Sie die PersonModel Klasse erweitert und Eigenschaft hinzugefügt Child haben:

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

Der Schlüsselpfad zum Namen des untergeordneten Elements wäre self.Child.Name oder einfach Child.Name (basierend auf der Verwendung des Schlüsselwerts).

Abrufen von Werten mithilfe der Schlüsselwertcodierung

Die ValueForKey Methode gibt den Wert für den angegebenen Schlüssel (als a NSString) relativ zur Instanz der KVC-Klasse zurück, die die Anforderung empfängt. Wenn Person es sich beispielsweise um eine Instanz der PersonModel oben definierten Klasse handelt:

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

Dadurch wird der Wert der Name Eigenschaft für diese Instanz zurückgegeben.PersonModel

Festlegen von Werten mithilfe der Schlüsselwertcodierung

Entsprechend wird der SetValueForKey Wert für den angegebenen Schlüssel (als a NSString) relativ zur Instanz der KVC-Klasse festgelegt, die die Anforderung empfängt. Verwenden Sie also erneut eine Instanz der PersonModel Klasse, wie unten dargestellt:

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

Würde den Wert der Name Eigenschaft in Jane Doe.

Beobachten von Wertänderungen

Mithilfe von Key-Value Observing (KVO) können Sie einen Beobachter an einen bestimmten Schlüssel einer KVC-kompatiblen Klasse anfügen und jedes Mal benachrichtigt werden, wenn der Wert für diesen Schlüssel geändert wird (entweder mithilfe von KVC-Techniken oder direkt auf die angegebene Eigenschaft im C#-Code zugreifen). Zum Beispiel:

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

Jedes Mal, wenn die Name Eigenschaft der Person Instanz der PersonModel Klasse geändert wird, wird der neue Wert in die Konsole geschrieben.

Weitere Informationen finden Sie in der Einführung in das Programmierhandbuch für Key-Value Observing von Apple.

Datenbindung

In den folgenden Abschnitten wird gezeigt, wie Sie eine Codierung von Schlüsselwerten und eine Kompatible Klasse für Schlüsselwerte verwenden können, um Daten an UI-Elemente im Schnittstellen-Generator von Xcode zu binden, anstatt Werte mithilfe von C#-Code zu lesen und zu schreiben. Auf diese Weise trennen Sie Ihr Datenmodell von den Ansichten, die zum Anzeigen verwendet werden, wodurch die Xamarin.Mac-Anwendung flexibler und einfacher Standard tain wird. Außerdem verringern Sie die Menge an Code, der geschrieben werden muss.

Definieren des Datenmodells

Bevor Sie ein UI-Element im Schnittstellen-Generator binden können, müssen Sie eine KVC/KVO-kompatible Klasse in Ihrer Xamarin.Mac-Anwendung definieren, um als Datenmodell für die Bindung zu fungieren. Das Datenmodell stellt alle Daten bereit, die auf der Benutzeroberfläche angezeigt werden, und empfängt alle Änderungen an den Daten, die der Benutzer in der Benutzeroberfläche während der Ausführung der Anwendung vornimmt.

Wenn Sie beispielsweise eine Anwendung schreiben, die eine Gruppe von Mitarbeitern verwaltet, können Sie die folgende Klasse verwenden, um das Datenmodell zu definieren:

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

Die meisten Features dieser Klasse wurden im Abschnitt "Schlüsselwertcodierung " weiter oben behandelt. Sehen wir uns jedoch einige bestimmte Elemente und einige Ergänzungen an, die es dieser Klasse ermöglichen, als Datenmodell für Arraycontroller und Strukturcontroller zu fungieren (die wir später zur Datenbindung von Strukturansichten, Gliederungsansichten und Sammlungsansichten verwenden).

Erstens, da ein Mitarbeiter möglicherweise ein Vorgesetzter ist, haben wir eine NSArray (insbesondere eine NSMutableArray Änderung der Werte) verwendet, damit die Mitarbeiter, die sie verwaltet haben, an sie angefügt werden können:

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

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

Hier sind zwei Punkte zu beachten:

  1. Wir haben anstelle NSMutableArray eines standardmäßigen C#-Arrays oder einer Standardauflistung verwendet, da dies eine Anforderung für die Datenbindung an AppKit-Steuerelemente wie Tabellenansichten, Gliederungsansichten und Auflistungen ist.
  2. Wir haben das Array von Mitarbeitern verfügbar gemacht, indem wir es zu NSArray Datenbindungszwecken umwandeln und den formatierten C#-Namen in eine formatierte C#-Datei umwandeln, Peoplezu einer, die von der Datenbindung erwartet wird, personModelArray in Der Form {class_name}Array (beachten Sie, dass das erste Zeichen klein geschrieben wurde).

Als Nächstes müssen wir einige speziell benennende öffentliche Methoden hinzufügen, um Arraycontroller und Strukturcontroller zu unterstützen:

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

Diese ermöglichen es den Controllern, die angezeigten Daten anzufordern und zu ändern. Wie oben dargestellt NSArray weisen diese eine sehr spezifische Benennungskonvention auf (die sich von den typischen C#-Benennungskonventionen unterscheidet):

  • addObject: - Fügt dem Array ein Objekt hinzu.
  • insertObject:in{class_name}ArrayAtIndex: – Wo {class_name} ist der Name Ihres Kurses. Diese Methode fügt ein Objekt in das Array bei einem bestimmten Index ein.
  • removeObjectFrom{class_name}ArrayAtIndex: – Wo {class_name} ist der Name Ihres Kurses. Mit dieser Methode wird das Objekt im Array bei einem bestimmten Index entfernt.
  • set{class_name}Array: – Wo {class_name} ist der Name Ihres Kurses. Mit dieser Methode können Sie das vorhandene Tragen durch eine neue ersetzen.

Innerhalb dieser Methoden haben wir Änderungen an dem Array in WillChangeValue und DidChangeValue Nachrichten für die KVO-Compliance umschlossen.

Da die Icon Eigenschaft auf den Wert der isManager Eigenschaft basiert, werden Änderungen an der isManager Eigenschaft möglicherweise nicht in den datengebundenen UI-Elementen (während der Icon KVO) widerzuspiegeln:

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

Um dies zu korrigieren, verwenden wir den folgenden Code:

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

Beachten Sie, dass der isManager Accessor zusätzlich zu seinem eigenen Schlüssel auch die WillChangeValue Und-Nachrichten DidChangeValue für den Icon Schlüssel sendet, damit auch die Änderung angezeigt wird.

Wir verwenden das PersonModel Datenmodell während des restlichen Artikels.

Einfache Datenbindung

Sehen wir uns mit unserem definierten Datenmodell ein einfaches Beispiel für die Datenbindung im Schnittstellen-Generator von Xcode an. Fügen wir beispielsweise ein Formular zu unserer Xamarin.Mac-Anwendung hinzu, die zum Bearbeiten des PersonModel oben definierten Formulars verwendet werden kann. Wir fügen einige Textfelder und ein Kontrollkästchen zum Anzeigen und Bearbeiten von Eigenschaften unseres Modells hinzu.

Als Erstes fügen wir einen neuen Ansichtscontroller zu unserer Datei "Main.storyboard" im Schnittstellen-Generator hinzu und nennen die KlasseSimpleViewController:

Adding a new view controller with a class named SimpleViewController.

Kehren Sie als Nächstes zu Visual Studio für Mac zurück, bearbeiten Sie die SimpleViewController.cs-Datei (die automatisch zu unserem Projekt hinzugefügt wurde), und machen Sie eine Instanz der PersonModel Datenbindung des Formulars verfügbar. Fügen Sie den folgenden Code hinzu:

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

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

Als Nächstes, wenn die Ansicht geladen wird, erstellen wir eine Instanz unserer PersonModel und füllen sie mit diesem Code auf:

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;

}

Jetzt müssen wir unser Formular erstellen, doppelklicken Sie auf die Datei "Main.storyboard ", um es zum Bearbeiten im Schnittstellen-Generator zu öffnen. Layout the form to look like the following:

Editing the storyboard in Xcode

Gehen Sie wie folgt vor, um das Formular an das PersonModel Formular zu binden, das über den Person Schlüssel verfügbar gemacht wird:

  1. Wählen Sie das Textfeld "Mitarbeitername" aus, und wechseln Sie zum Inspektor für Bindungen.

  2. Aktivieren Sie das Kontrollkästchen "Binden an ", und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Geben Sie self.Person.Name als Nächstes den Schlüsselpfad ein:

    Entering self dot person dot name for the Key Path.

  3. Aktivieren Sie das Textfeld "Beruf " und aktivieren Sie das Kontrollkästchen "Binden an ", und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Geben Sie self.Person.Occupation als Nächstes den Schlüsselpfad ein:

    Entering self dot Person dot Occupation for the Key Path.

  4. Aktivieren Sie das Kontrollkästchen "Mitarbeiter" als Vorgesetzter , und aktivieren Sie das Kontrollkästchen "Binden an ", und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Geben Sie self.Person.isManager als Nächstes den Schlüsselpfad ein:

    Entering self dot Person dot isManager for the Key Path.

  5. Wählen Sie das Feld "Anzahl der verwalteten Mitarbeiter" aus, und aktivieren Sie das Kontrollkästchen "Binden an ", und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Geben Sie self.Person.NumberOfEmployees als Nächstes den Schlüsselpfad ein:

    Entering self dot Person dot NumberOfEmployees for the Key Path.

  6. Wenn der Mitarbeiter kein Vorgesetzter ist, möchten wir die Anzahl der verwalteten Mitarbeiterbezeichnung und das Textfeld ausblenden.

  7. Wählen Sie die Anzahl der verwalteten Bezeichnungen für Mitarbeiter aus, erweitern Sie den ausgeblendeten Turndown, und aktivieren Sie das Kontrollkästchen "Binden an ", und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Geben Sie self.Person.isManager als Nächstes den Schlüsselpfad ein:

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

  8. Wählen Sie NSNegateBoolean aus der Dropdownliste "Werttransformer" aus:

    Selecting the NSNegateBoolean key transformation

  9. Dadurch wird die Datenbindung mitgeteilt, dass die Bezeichnung ausgeblendet wird, wenn der Wert der isManager Eigenschaft lautet false.

  10. Wiederholen Sie die Schritte 7 und 8 für das Feld "Anzahl der mitarbeiterverwalteten Textfeld".

  11. Speichern Sie Ihre Änderungen, und kehren Sie zu Visual Studio für Mac zurück, um mit Xcode zu synchronisieren.

Wenn Sie die Anwendung ausführen, füllen die Werte aus der Person Eigenschaft automatisch unser Formular aus:

Showing an auto-populated form

Alle Änderungen, die die Benutzer am Formular vornehmen, werden in die Person Eigenschaft im Ansichtscontroller zurückgeschrieben. Das Aufheben der Auswahl von "Mitarbeiter" ist z. B. ein Manager aktualisiert die Person Instanz unserer PersonModel Und das Feld "Anzahl der verwalteten Mitarbeiter" und "Textfeld" wird automatisch ausgeblendet (über die Datenbindung):

Hiding the number of employees for non-managers

Datenbindung für Tabellenansichten

Nachdem wir nun über die Grundlagen der Datenbindung verfügen, sehen wir uns eine komplexere Datenbindungsaufgabe mithilfe eines Arraycontrollers und einer Datenbindung an eine Tabellenansicht an. Weitere Informationen zum Arbeiten mit Tabellenansichten finden Sie in der Dokumentation zu Tabellenansichten .

Als Erstes fügen wir einen neuen Ansichtscontroller zu unserer Datei "Main.storyboard" im Schnittstellen-Generator hinzu und nennen die KlasseTableViewController:

Adding a new view controller with a class named TableViewController.

Als Nächstes bearbeiten wir die TableViewController.cs Datei (die automatisch zu unserem Projekt hinzugefügt wurde) und machen ein Array (NSArray) von PersonModel Klassen verfügbar, an die wir unser Formular binden werden. Fügen Sie den folgenden Code hinzu:

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

Genau wie in der PersonModel klasse oben im Abschnitt "Definieren Ihres Datenmodells " haben wir vier speziell benannte öffentliche Methoden verfügbar gemacht, sodass der ArrayController Daten aus unserer Sammlung PersonModelslesen und schreiben kann.

Als Nächstes, wenn die Ansicht geladen wird, müssen wir unser Array mit diesem Code auffüllen:

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

}

Jetzt müssen wir unsere Tabellenansicht erstellen, doppelklicken Sie auf die Datei "Main.storyboard ", um sie zum Bearbeiten im Schnittstellen-Generator zu öffnen. Layouten Sie die Tabelle so, dass sie etwa wie folgt aussieht:

Laying out a new table view

Wir müssen einen Arraycontroller hinzufügen, um gebundene Daten zu unserer Tabelle bereitzustellen. Gehen Sie wie folgt vor:

  1. Ziehen Sie einen Arraycontroller aus dem Bibliotheksinspektor auf den Schnittstellen-Editor:

    Selecting an Array Controller from the Library

  2. Wählen Sie den Arraycontroller in der Schnittstellenhierarchie aus, und wechseln Sie zum Attributinspektor:

    Selecting the Attributes Inspector

  3. Geben Sie die EINGABETASTE PersonModel für den Klassennamen ein, klicken Sie auf die Schaltfläche "Plus ", und fügen Sie drei Schlüssel hinzu. Benennen Sie sie Name, Occupation und isManager:

    Adding the required key paths to the Object Controller.

  4. Dies teilt dem Arraycontroller mit, was er für die Verwaltung eines Arrays verwendet, und welche Eigenschaften verfügbar gemacht werden sollen (über Schlüssel).

  5. Wechseln Sie zum Inspektor für Bindungen, und wählen Sie unter "Inhaltsarray" die Option "Binden an" und "Tabellenansichtscontroller" aus. Geben Sie einen Modellschlüsselpfad von self.personModelArray:

    Entering a key path

  6. Dadurch wird der Arraycontroller mit dem Array PersonModels verbunden, das wir auf unserem Ansichtscontroller verfügbar gemacht haben.

Jetzt müssen wir unsere Tabellenansicht an den Arraycontroller binden, gehen Sie wie folgt vor:

  1. Wählen Sie die Tabellenansicht und den Bindungsinspektor aus:

    Selecting the Table View and Binding Inspector.

  2. Wählen Sie unter dem Turndown "Tabelleninhalte " die Option "Binden an " und "Arraycontroller" aus. Eingabe arrangedObjects für das Feld "Controllerschlüssel ":

    Defining the controller key

  3. Wählen Sie die Tabellenansichtszelle unter der Spalte "Mitarbeiter " aus. Wählen Sie im Bindungen-Inspektor unter demWert-Turndown die Option "Binden an" und "Tabellenzellenansicht" aus. Geben Sie objectValue.Name für den Modellschlüsselpfad ein:

    Setting the model key path for the Employee column.

  4. objectValue ist der Aktuelle PersonModel im Array, das vom Arraycontroller verwaltet wird.

  5. Wählen Sie die Tabellenansichtszelle unter der Spalte "Beruf " aus. Wählen Sie im Bindungen-Inspektor unter demWert-Turndown die Option "Binden an" und "Tabellenzellenansicht" aus. Geben Sie objectValue.Occupation für den Modellschlüsselpfad ein:

    Setting the model key path for the Occupation column.

  6. Speichern Sie Ihre Änderungen, und kehren Sie zu Visual Studio für Mac zurück, um mit Xcode zu synchronisieren.

Wenn wir die Anwendung ausführen, wird die Tabelle mit unserem Array von PersonModels:

Running the application, which populates the array of PersonModels.

Datenbindung in der Gliederungsansicht

Die Datenbindung für eine Gliederungsansicht ähnelt sehr der Bindung für eine Tabellenansicht. Der Hauptunterschied besteht darin, dass wir einen Strukturcontroller anstelle eines Arraycontrollers verwenden, um die gebundenen Daten für die Gliederungsansicht bereitzustellen. Weitere Informationen zum Arbeiten mit Gliederungsansichten finden Sie in unserer Dokumentation zu Gliederungsansichten .

Als Erstes fügen wir einen neuen Ansichtscontroller zu unserer Datei "Main.storyboard" im Schnittstellen-Generator hinzu und nennen die KlasseOutlineViewController:

Adding a new view controller with a class named OutlineViewController.

Als Nächstes bearbeiten wir die OutlineViewController.cs Datei (die automatisch zu unserem Projekt hinzugefügt wurde) und machen ein Array (NSArray) von PersonModel Klassen verfügbar, an die wir unser Formular binden werden. Fügen Sie den folgenden Code hinzu:

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

Genau wie im PersonModel Abschnitt "Definieren Des Datenmodells " haben wir vier speziell benannte öffentliche Methoden verfügbar gemacht, damit der Strukturcontroller Daten aus unserer Sammlung PersonModelslesen und schreiben kann.

Als Nächstes, wenn die Ansicht geladen wird, müssen wir unser Array mit diesem Code auffüllen:

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

}

Jetzt müssen wir unsere Gliederungsansicht erstellen, doppelklicken Sie auf die Datei "Main.storyboard ", um sie zum Bearbeiten im Schnittstellen-Generator zu öffnen. Layouten Sie die Tabelle so, dass sie etwa wie folgt aussieht:

Creating the outline view

Wir müssen einen Strukturverantwortlichen hinzufügen, um gebundene Daten zu unserer Gliederung bereitzustellen, gehen Sie wie folgt vor:

  1. Ziehen Sie einen Strukturcontroller aus dem Bibliotheksinspektor auf den Schnittstellen-Editor:

    Selecting a Tree Controller from the Library

  2. Wählen Sie den Strukturcontroller in der Schnittstellenhierarchie aus, und wechseln Sie zum Attributinspektor:

    Selecting the Attribute Inspector

  3. Geben Sie die EINGABETASTE PersonModel für den Klassennamen ein, klicken Sie auf die Schaltfläche "Plus ", und fügen Sie drei Schlüssel hinzu. Benennen Sie sie Name, Occupation und isManager:

    Adding the required key paths for PersonModel.

  4. Dadurch wird dem Strukturcontroller mitgeteilt, was er für die Verwaltung eines Arrays verwendet, und welche Eigenschaften verfügbar gemacht werden sollen (über Schlüssel).

  5. Geben Sie unter dem Abschnitt "Strukturcontroller" die Zeichenfolge "Untergeordnete Elemente" ein, geben Sie NumberOfEmployees unter "Anzahl" ein, und geben Sie unter "Blatt" einisEmployee:personModelArray

    Setting the Tree Controller key paths

  6. Dadurch wird dem Strukturcontroller mitgeteilt, wo untergeordnete Knoten gefunden werden sollen, wie viele untergeordnete Knoten vorhanden sind und ob der aktuelle Knoten untergeordnete Knoten aufweist.

  7. Wechseln Sie zum Inspektor für Bindungen, und wählen Sie unter "Inhaltsarray" die Option "An den Besitzer der Datei binden" und "Dateibesitzer" aus. Geben Sie einen Modellschlüsselpfad von self.personModelArray:

    Editing the key path

  8. Dadurch wird der Strukturcontroller mit dem Array PersonModels verknüpft, das wir auf unserem Ansichtscontroller verfügbar gemacht haben.

Jetzt müssen wir unsere Gliederungsansicht an den Strukturcontroller binden, gehen Sie wie folgt vor:

  1. Wählen Sie die Gliederungsansicht aus, und wählen Sie im Bindungsinspektor Folgendes aus:

    Selecting the Outline View and Binding Inspector.

  2. Wählen Sie im Turndown der Gliederungsansicht "Inhalt " die Option "Binden an " und "Strukturcontroller" aus. Eingabe arrangedObjects für das Feld "Controllerschlüssel ":

    Setting the controller key

  3. Wählen Sie die Tabellenansichtszelle unter der Spalte "Mitarbeiter " aus. Wählen Sie im Bindungen-Inspektor unter demWert-Turndown die Option "Binden an" und "Tabellenzellenansicht" aus. Geben Sie objectValue.Name für den Modellschlüsselpfad ein:

    Entering the model key path value objectValue dot Name.

  4. objectValue ist der Aktuelle PersonModel im Array, das vom Strukturcontroller verwaltet wird.

  5. Wählen Sie die Tabellenansichtszelle unter der Spalte "Beruf " aus. Wählen Sie im Bindungen-Inspektor unter demWert-Turndown die Option "Binden an" und "Tabellenzellenansicht" aus. Geben Sie objectValue.Occupation für den Modellschlüsselpfad ein:

    Entering the model key path value objectValue dot Occupation.

  6. Speichern Sie Ihre Änderungen, und kehren Sie zu Visual Studio für Mac zurück, um mit Xcode zu synchronisieren.

Wenn wir die Anwendung ausführen, wird die Gliederung mit unserem Array von PersonModels:

Running the application, which populates our array of PersonModels.

Datenbindung der Sammlungsansicht

Die Datenbindung mit einer Sammlungsansicht ähnelt der Bindung mit einer Tabellenansicht, da ein Arraycontroller verwendet wird, um Daten für die Sammlung bereitzustellen. Da die Sammlungsansicht nicht über ein voreingestelltes Anzeigeformat verfügt, ist mehr Arbeit erforderlich, um Feedback zur Benutzerinteraktion bereitzustellen und die Benutzerauswahl nachzuverfolgen.

Wichtig

Aufgrund eines Problems in Xcode 7 und macOS 10.11 (und höher) können Sammlungsansichten nicht innerhalb einer Storyboarddateien (Storyboard) verwendet werden. Daher müssen Sie weiterhin XIB-Dateien verwenden, um Ihre Sammlungsansichten für Ihre Xamarin.Mac-Apps zu definieren. Weitere Informationen finden Sie in der Dokumentation zu Sammlungsansichten .

Debuggen systemeigener Abstürze

Wenn Sie einen Fehler in Ihren Datenbindungen machen, kann ein systemeigener Absturz in nicht verwaltetem Code auftreten und dazu führen, dass Ihre Xamarin.Mac-Anwendung vollständig mit einem SIGABRT Fehler fehlschlägt:

Example of a native crash dialog box

Normalerweise gibt es vier Hauptursachen für systemeigene Abstürze während der Datenbindung:

  1. Ihr Datenmodell erbt nicht von NSObject oder einer Unterklasse von NSObject.
  2. Sie haben Ihre Eigenschaft nicht für Objective-C die Verwendung des [Export("key-name")] Attributs verfügbar gemacht.
  3. Sie haben keine Änderungen am Wert des Accessors in WillChangeValue und DidChangeValue Methodenaufrufe umbrochen (angeben denselben Schlüssel wie das Export Attribut).
  4. Sie haben einen falschen oder falsch eingegebenen Schlüssel im Bindungsinspektor im Schnittstellen-Generator.

Decodieren eines Absturzes

Lassen Sie uns einen systemeigenen Absturz in unserer Datenbindung verursachen, damit wir zeigen können, wie sie gefunden und behoben werden können. Im Schnittstellen-Generator ändern wir unsere Bindung des ersten Bezeichnungsbeispiels in der Sammlungsansicht von Name :Title

Editing the binding key

Lassen Sie uns die Änderung speichern, zurück zu Visual Studio für Mac wechseln, um mit Xcode zu synchronisieren und unsere Anwendung auszuführen. Wenn die Sammlungsansicht angezeigt wird, stürzt die Anwendung momentan mit einem SIGABRT Fehler ab (wie in der Anwendungsausgabe in Visual Studio für Mac dargestellt), da die PersonModel Eigenschaft mit dem Schlüssel Titlenicht verfügbar gemacht wird:

Example of a binding error

Wenn ein Bildlauf zum Anfang des Fehlers in der Anwendungsausgabe erfolgt , wird der Schlüssel zum Beheben des Problems angezeigt:

Finding the issue in the error log

Diese Zeile teilt uns mit, dass der Schlüssel Title für das Objekt, an das wir binden, nicht vorhanden ist. Wenn wir die Bindung wieder Name in den Schnittstellen-Generator ändern, speichern, synchronisieren, neu erstellen und ausführen, wird die Anwendung wie erwartet ausgeführt.

Zusammenfassung

Dieser Artikel hat einen detaillierten Blick auf die Arbeit mit datenbindung und Key-Value-Codierung in einer Xamarin.Mac-Anwendung gemacht. Zunächst wurde eine C#-Klasse Objective-C mithilfe von Schlüsselwertcodierung (Key-Value Coding, KVC) und Key-Value Observing (KVO) bereitgestellt. Als Nächstes wurde gezeigt, wie sie eine KVO-kompatible Klasse und Datenbindung an UI-Elemente im Schnittstellen-Generator von Xcode verwenden. Schließlich wurde eine komplexe Datenbindung mithilfe von Arraycontrollern und Strukturcontrollern gezeigt.