Datenbindung und Schlüsselwertcodierung in Xamarin.Mac

In diesem Artikel wird die Verwendung von Schlüsselwertcodierung und Schlüsselwertbeachtung behandelt, um die Datenbindung an UI-Elemente im Xcode-Schnittstellen-Generator zu ermöglichen.

Überblick

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

Mithilfe von Schlüsselwertcodierungs- und Datenbindungstechniken in Ihrer Xamarin.Mac-Anwendung können Sie die Menge des Codes, den Sie schreiben und verwalten müssen, erheblich verringern, um mit UI-Elementen zu füllen und zu arbeiten. Sie haben auch den Vorteil, ihre Backing-Daten (Data Model) von Ihrer Front-End-Benutzeroberfläche (Model-View-Controller) weiter zu entkoppeln, wodurch das Verwalten, flexibleres Anwendungsdesign erleichtert wird.

An example of the running app

In diesem Artikel werden die Grundlagen der Arbeit mit Schlüsselwertcodierung und Datenbindung in einer Xamarin.Mac-Anwendung behandelt. Es wird dringend vorgeschlagen, dass Sie zuerst den Artikel "Hello" und "Mac" durchlaufen, insbesondere die Einführung in Xcode- und Interface-Generator- undAktionsabschnitte , da sie wichtige Konzepte und Techniken behandelt, die wir in diesem Artikel verwenden werden.

Möglicherweise möchten Sie sich die C#-Klassen /Methoden zum Abschnitt "Xamarin.Mac Internals" ansehen, und sie erläutert die und attribute, die verwendet werden, um Objective-CIhre C#-Klassen an Objective-C Objekte und UI-Elemente zu verkabeln.RegisterExport

Was ist die Schlüsselwertcodierung

Key-Value-Codierung (KVC) ist ein Mechanismus für den Zugriff auf die Eigenschaften eines Objekts indirekt, indem Schlüssel (speziell formatierte Zeichenfolgen) verwendet werden, um Eigenschaften zu identifizieren, anstatt sie über Instanzvariablen oder Accessormethoden (get/set) zu identifizieren. Durch Implementieren von kompatiblen Schlüsselwert-Zugriffsoren 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, Kakaobindungen und Skriptbarkeit.

Mithilfe von Schlüsselwertcodierungs- und Datenbindungstechniken in Ihrer Xamarin.Mac-Anwendung können Sie die Menge des Codes, den Sie schreiben und verwalten müssen, erheblich verringern, um mit UI-Elementen zu füllen und zu arbeiten. Sie haben auch den Vorteil, ihre Backing-Daten (Data Model) von Ihrer Front-End-Benutzeroberfläche (Model-View-Controller) weiter zu entkoppeln, wodurch das Verwalten, flexibleres Anwendungsdesign erleichtert wird.

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 Attribut die [Register("PersonModel")] Klasse und macht es verfügbar Objective-C. Anschließend muss die Klasse von (oder einer Unterklasse, die von NSObjecterbt) erben NSObject , mehrere Basismethoden hinzugefügt werden, die es der Klasse ermöglichen, KVC-konform zu sein. Als Nächstes stellt das [Export("Name")] Attribut die Name Eigenschaft bereit und definiert den Schlüsselwert, der später verwendet wird, um auf die Eigenschaft über KVC- und KVO-Techniken zuzugreifen.

Schließlich muss der Accessor Änderungen an dem Wert der Eigenschaft Key-Value beobachtet werden können. Der Accessor muss Änderungen an seinem Wert in WillChangeValue und DidChangeValue den Methodenaufrufen umschließen (angeben denselben Schlüssel wie das Export Attribut). Beispiel:

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

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

Weitere Informationen finden Sie im Leitfaden zur Key-Value-Programmierung 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 Accessor-Methode in einem kompatiblen Schlüsselwertobjekt. Schlüssel müssen ASCII-Codierung verwenden, beginnen normalerweise mit einem Kleinbuchstaben und enthalten möglicherweise keine Leerzeichen. Im folgenden Beispiel Name wäre also ein Schlüsselwert der Name Eigenschaft der PersonModel Klasse. Der Schlüssel und der Name der eigenschaft, die sie verfügbar machen, müssen jedoch in den meisten Fällen nicht identisch sein.

Ein Schlüsselpfad ist eine Zeichenfolge von punkttrennten Schlüsseln, die verwendet werden, um eine Hierarchie von Objekteigenschaften anzugeben, die durchlaufen werden. 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 dieselbe Weise verwenden Sie Punktnotation, um ein Objekt und dessen Eigenschaften in einer C#-Klasse zu durchlaufen.

Wenn Sie beispielsweise 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 (as a NSString) relativ zur Instanz der KVC-Klasse zurück, die die Anforderung empfängt. Wenn Person es sich beispielsweise um eine Instanz der oben definierten PersonModel 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

Ebenso legt der SetValueForKey Wert für den angegebenen Schlüssel (as a NSString) relativ zur Instanz der KVC-Klasse, die die Anforderung empfängt, fest. So erneut, verwenden Sie eine Instanz der PersonModel Klasse, wie unten gezeigt:

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

Würde den Wert der Name Eigenschaft auf Jane Doeändern.

Beobachten von Wertänderungen

Mithilfe der Key-Value-Beobachtung (KVO) können Sie einen Beobachter an einen bestimmten Schlüssel einer KVC-kompatiblen Klasse anfügen und jederzeit 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). 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)
});

Wenn die Eigenschaft der Person Instanz der PersonModel Klasse geändert wird, wird der Name neue Wert nun in die Konsole geschrieben.

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

Datenbindung

In den folgenden Abschnitten wird gezeigt, wie Sie mithilfe eines Schlüsselwertcodierungs- und Schlüsselwert-Codierungs- und Schlüsselwerts konforme Klasse Daten an UI-Elemente in Xcode-Schnittstellen-Generator binden können, anstatt Werte mithilfe des C#-Codes zu lesen und zu schreiben. Auf diese Weise trennen Sie Ihr Datenmodell von den Ansichten, die verwendet werden, um sie anzuzeigen, wodurch die Xamarin.Mac-Anwendung flexibler und einfacher zu verwalten ist. Sie verringern auch die Menge des Codes, der geschrieben werden muss.

Definieren des Datenmodells

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

Wenn Sie beispielsweise eine Anwendung schreiben, die eine Gruppe von Mitarbeitern verwaltet hat, 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 obigen Abschnitt " Schlüsselwertcodierung " behandelt. Sehen wir uns jedoch einige bestimmte Elemente und einige Ergänzungen an, die dazu gemacht wurden, diese Klasse als Datenmodell für Arraycontroller und Strukturcontroller zu handeln (was wir später verwenden werden, um Strukturansichten, Gliederungsansichten und Auflistungsansichten zu binden).

Erstens, da ein Mitarbeiter ein Manager sein kann, haben wir eine NSArray (insbesondere so NSMutableArray dass die Werte geändert werden können) verwendet, um den Mitarbeitern zu ermöglichen, die sie an sie angefügt haben:

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

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

Zwei Dinge, die hier zu beachten sind:

  1. Wir haben anstelle NSMutableArray eines standardmäßigen C#-Arrays oder einer Auflistung verwendet, da dies eine Anforderung für die Datenbindung an AppKit-Steuerelemente wie Tabellenansichten, Gliederungsansichten und Sammlungen ist.
  2. Wir haben das Array von Mitarbeitern verfügbar gemacht, indem sie sie zu datenbindungszwecken verwerfen NSArray und den C#-formatierten Namen geändert haben, in einem, der die Datenbindung erwartet, PeoplepersonModelArray in form {class_name}Array (beachten Sie, dass das erste Zeichen Kleinschreibung vorgenommen wurde).

Als Nächstes müssen wir einige spezielle ö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");
}

Damit können die Controller die angezeigten Daten anfordern und ändern. Wie oben verfügbar NSArray gemacht, verfügen diese über eine sehr spezifische Benennungskonvention (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 Ihrer Klasse. Diese Methode fügt ein Objekt in das Array bei einem bestimmten Index ein.
  • removeObjectFrom{class_name}ArrayAtIndex: - Wo {class_name} ist der Name Ihrer Klasse. Diese Methode entfernt das Objekt im Array bei einem bestimmten Index.
  • set{class_name}Array: - Wo {class_name} ist der Name Ihrer Klasse. Mit dieser Methode können Sie den vorhandenen Transport durch eine neue ersetzen.

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

Schließlich, da die Icon Eigenschaft auf den Wert der Eigenschaft basiert, werden Änderungen an der isManager Eigenschaft möglicherweise nicht in den isManagerIcon Für Data Bound UI-Elementen (während des KVO) angezeigt:

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

Um das 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 DidChangeValue Nachrichten für den Icon Schlüssel sendet, sodass die Änderung ebenfalls angezeigt wird.

Wir verwenden das PersonModel Datenmodell im gesamten Rest dieses Artikels.

Einfache Datenbindung

Mit unserem definierten Datenmodell sehen wir uns ein einfaches Beispiel für die Datenbindung im Xcode-Schnittstellen-Generator an. Lassen Sie uns beispielsweise ein Formular zu unserer Xamarin.Mac-Anwendung hinzufügen, die zum Bearbeiten des PersonModel oben definierten Formulars verwendet werden kann. Wir fügen ein paar Textfelder und ein Kontrollkästchen hinzu, um Eigenschaften unseres Modells anzuzeigen und zu bearbeiten.

Zunächst fügen wir einen neuen Ansichtscontroller zu unserer Main.storyboard-Datei im Schnittstellen-Generator hinzu und benennen dessen Klasse SimpleViewController:

Adding a new view controller with a class named SimpleViewController.

Kehren Sie als Nächstes zur Visual Studio für Mac zurück, bearbeiten Sie die Datei SimpleViewController.cs (die automatisch zu unserem Projekt hinzugefügt wurde), und stellen Sie eine Instanz der PersonModel Datenbindung unseres Formulars bereit. 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");
    }
}

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 sie für die Bearbeitung im Schnittstellen-Generator zu öffnen. Layout des Formulars so wie folgt:

Editing the storyboard in Xcode

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

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

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

    Entering self dot person dot name for the Key Path.

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

    Entering self dot Person dot Occupation for the Key Path.

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

    Entering self dot Person dot isManager for the Key Path.

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

    Entering self dot Person dot NumberOfEmployees for the Key Path.

  6. Wenn der Mitarbeiter kein Manager ist, möchten wir die Anzahl der verwalteten Mitarbeiter und Das Textfeld ausblenden.

  7. Wählen Sie die Anzahl verwalteter Mitarbeiterbezeichnung aus, erweitern Sie die ausgeblendete Sperre, und aktivieren Sie das Kontrollkästchen "Binden" , und wählen Sie "Einfacher Ansichtscontroller " aus der Dropdownliste aus. Nächste Eingabetaste self.Person.isManager für den Schlüsselpfad:

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

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

    Selecting the NSNegateBoolean key transformation

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

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

  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 auf:

Showing an auto-populated form

Alle Änderungen, die die Benutzer am Formular vornehmen, werden in die Person Eigenschaft im Ansichtscontroller zurückgeschrieben. Beispielsweise wird die Auswahl des Mitarbeiters durch einen Vorgesetzten aktualisiert, PersonPersonModel und die Anzahl der verwalteten Bezeichnungen und das Textfeld "Mitarbeiter" werden automatisch ausgeblendet (über die Datenbindung):

Hiding the number of employees for non-managers

Datenbindung in Tabellenansicht

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 .

Zunächst fügen wir unserem Main.storyboard-Datei im Interface Builder einen neuen Ansichtscontroller hinzu und benennen 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 stellen ein Array (NSArray) von PersonModel Klassen bereit, an die wir unser Formular binden. 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 Ihres Datenmodells " haben wir vier speziell benannte öffentliche Methoden verfügbar gemacht, damit 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 wie folgt aussieht:

Laying out a new table view

Wir müssen einen Arraycontroller hinzufügen, um gebundene Daten an unsere Tabelle bereitzustellen, gehen Sie folgendermaßen vor:

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

    Selecting an Array Controller from the Library

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

    Selecting the Attributes Inspector

  3. Geben Sie 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, welche Eigenschaften (über Schlüssel) verfügbar gemacht werden sollen.

  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 ein: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 im Turndown " Tabelleninhalte " die Option "Binden an " und "Arraycontroller" aus. Geben Sie für das Feld "Controllerschlüssel" einarrangedObjects:

    Defining the controller key

  3. Wählen Sie die Zelle "Tabellenansicht " unter der Spalte "Mitarbeiter " aus. Wählen Sie im Bindungenprüfungsinspektor unter dem Wert-Turndown die Option "Binden an " und " Tabellenzellenansicht" aus. Geben Sie für den Modellschlüsselpfad einobjectValue.Name:

    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 Zelle "Tabellenansicht" unter der Spalte "Besetzung " aus. Wählen Sie im Bindungenprüfungsinspektor unter dem Wert-Turndown die Option "Binden an " und " Tabellenzellenansicht" aus. Geben Sie für den Modellschlüsselpfad einobjectValue.Occupation:

    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 der Bindung an eine Tabellenansicht. Der wichtigste Unterschied besteht darin, dass wir einen Strukturcontroller anstelle eines Arraycontrollers verwenden, um die gebundenen Daten an die Gliederungsansicht bereitzustellen. Weitere Informationen zum Arbeiten mit Gliederungsansichten finden Sie in der Dokumentation zu Gliederungsansichten .

Zunächst fügen wir unserem Main.storyboard-Datei im Interface Builder einen neuen Ansichtscontroller hinzu und benennen die KlasseOutlineViewController:

Adding a new view controller with a class named OutlineViewController.

Als Nächstes bearbeiten wir die Datei OutlineViewController.cs (die automatisch zu unserem Projekt hinzugefügt wurde) und machen ein Array (NSArray) von PersonModel Klassen verfügbar, an die wir unser Formular binden. 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 Ihres 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 wie folgt aussieht:

Creating the outline view

Wir müssen einen Strukturverantwortlichen hinzufügen, um gebundene Daten an unsere Gliederung bereitzustellen, gehen Sie folgendermaßen 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 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. Dies teilt dem Strukturcontroller mit, was er ein Array verwaltet, und welche Eigenschaften (über Schlüssel) verfügbar gemacht werden sollen.

  5. Geben Sie personModelArray im Abschnitt "Strukturcontroller" unter "Untergeordnete Elemente" unter "Anzahl" ein, und geben NumberOfEmployees Sie unter "Blatt" ein:isEmployee

    Setting the Tree Controller key paths

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

  7. Wechseln Sie zum Inspektor für Bindungen, und wählen Sie unter "Inhaltsarray" die Option "Bindung an" und "Besitzer der Datei" aus. Geben Sie einen Modellschlüsselpfad ein: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 :

    Selecting the Outline View and Binding Inspector.

  2. Wählen Sie unter dem Turndown für Gliederungsansichtsinhalte die Option "Binden an " und " Strukturcontroller" aus. Geben Sie für das Feld "Controllerschlüssel" einarrangedObjects:

    Setting the controller key

  3. Wählen Sie die Zelle "Tabellenansicht " unter der Spalte "Mitarbeiter " aus. Wählen Sie im Bindungenprüfungsinspektor unter dem Wert-Turndown die Option "Binden an " und " Tabellenzellenansicht" aus. Geben Sie für den Modellschlüsselpfad einobjectValue.Name:

    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 Zelle "Tabellenansicht" unter der Spalte "Besetzung " aus. Wählen Sie im Bindungenprüfungsinspektor unter dem Wert-Turndown die Option "Binden an " und " Tabellenzellenansicht" aus. Geben Sie für den Modellschlüsselpfad einobjectValue.Occupation:

    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 ist sehr ähnlich wie die 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 Benutzerinteraktionsfeedback 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)-Dateien 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

Fehler bei Ihren Datenbindungen können zu einem nativen Absturz im nicht verwalteten Code führen und dazu führen, dass Ihre Xamarin.Mac-Anwendung vollständig mit einem SIGABRT Fehler fehlschlägt:

Example of a native crash dialog box

Bei der Datenbindung gibt es in der Regel vier Hauptursachen für systemeigene Abstürze:

  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 an dem Wert des Zugriffsgebers in WillChangeValue und DidChangeValue Methodenaufrufen umgebrochen (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 der ersten Bezeichnung im Beispiel für die Sammlungsansicht von Name :Title

Editing the binding key

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

Example of a binding error

Wenn der Fehler in der Anwendungsausgabe ganz oben angezeigt wird, können wir den Schlüssel zur Lösung des Problems sehen:

Finding the issue in the error log

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

Zusammenfassung

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