Привязка данных и кодирование ключей в Xamarin.Mac

В этой статье описывается использование кодирования ключа и наблюдения за ключом, чтобы обеспечить привязку данных к элементам пользовательского интерфейса в построителе интерфейсов Xcode.

Обзор

При работе с C# и .NET в приложении Xamarin.Mac у вас есть доступ к тем же методам кодирования с ключом и привязкой данных, в которых работает Objective-C разработчик и Xcode . Так как Xamarin.Mac интегрируется непосредственно с Xcode, вы можете использовать построитель интерфейсов Xcode для привязки данных с элементами пользовательского интерфейса вместо написания кода.

Используя методы кодирования с ключом и привязки данных в приложении Xamarin.Mac, вы можете значительно уменьшить объем кода, который необходимо написать и поддерживать для заполнения и работы с элементами пользовательского интерфейса. Кроме того, вы можете дополнительно отсогласовать резервные данные (модель данных) с интерфейсного пользовательского интерфейса (model-View-Controller), что упрощает обслуживание, более гибкое проектирование приложений.

An example of the running app

В этой статье мы рассмотрим основы работы с кодом ключа и привязкой данных в приложении Xamarin.Mac. Настоятельно рекомендуется сначала ознакомиться со статьей Hello, Mac , в частности в разделах "Введение в Xcode" и "Конструктор интерфейсов" и "Торговых точек" и "Действия ", поскольку рассматриваются основные понятия и методы, которые мы будем использовать в этой статье.

Возможно, вам потребуется ознакомиться с классами И методами C# вObjective-Cразделе документа Xamarin.Mac Internals, а также объяснить RegisterExport и атрибуты, используемые для подключения классов C# к Objective-C объектам и элементам пользовательского интерфейса.

Что такое кодирование ключа-значение

Кодирование ключевого значения (KVC) — это механизм для косвенного доступа к свойствам объекта с помощью ключей (специально отформатированных строк) для идентификации свойств вместо доступа к ним через переменные экземпляра или методы доступа (get/set). Реализуя средства доступа, соответствующие ключу значения, в приложении Xamarin.Mac, вы получаете доступ к другим функциям macOS (ранее известной как OS X), таким как наблюдение за ключевым значением (KVO), привязка данных, основные данные, привязки Какао и скрипты.

Используя методы кодирования с ключом и привязки данных в приложении Xamarin.Mac, вы можете значительно уменьшить объем кода, который необходимо написать и поддерживать для заполнения и работы с элементами пользовательского интерфейса. Кроме того, вы можете дополнительно отсогласовать резервные данные (модель данных) с интерфейсного пользовательского интерфейса (model-View-Controller), что упрощает обслуживание, более гибкое проектирование приложений.

Например, давайте рассмотрим следующее определение класса соответствующего объекта 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 ()
        {
        }
    }
}

Во-первых, [Register("PersonModel")] атрибут регистрирует класс и предоставляет его Objective-C. Затем класс должен наследовать от NSObject (или подкласса, наследуемого от NSObject), это добавляет несколько базовых методов, которые позволяют классу соответствовать KVC. [Export("Name")] Затем атрибут предоставляет Name свойство и определяет значение ключа, которое позже будет использоваться для доступа к свойству с помощью методов KVC и KVO.

Наконец, чтобы иметь возможность быть key-Value Observed изменения значения свойства, метод доступа должен упаковывать изменения в его значение и WillChangeValue вызовы метода (указывая тот же ключ, что Export и DidChangeValue атрибут). Например:

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

Этот шаг очень важен для привязки данных в построителе интерфейсов Xcode (как показано далее в этой статье).

Дополнительные сведения см. в руководстве по программированию программирования на основе ключевых значений Apple.

Ключи и пути ключей

Ключ — это строка, определяющая определенное свойство объекта. Как правило, ключ соответствует имени метода доступа в объекте, совместимом с ключом- значением. Ключи должны использовать кодировку ASCII, обычно начинаться с строчной буквы и не содержать пробелы. Таким образом, учитывая приведенный выше пример, Name будет значение Name ключа свойства PersonModel класса. Ключ и имя свойства, которое они предоставляют, не должны совпадать, однако в большинстве случаев они являются одинаковыми.

Путь к ключу — это строка разделенных точками ключей, используемая для указания иерархии свойств объекта для обхода. Свойство первого ключа в последовательности относительно приемника, и каждый последующий ключ вычисляется относительно значения предыдущего свойства. Таким же образом вы используете нотацию точек для обхода объекта и его свойств в классе C#.

Например, если вы расширили PersonModel класс и добавили 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 ()
        {
        }
    }
}

Ключ пути к имени ребенка будет self.Child.Name или просто Child.Name (в зависимости от того, как используется значение ключа).

Получение значений с помощью кодирования "ключ-значение"

Метод ValueForKey возвращает значение указанного ключа (как a NSString), относительно экземпляра класса KVC, получающего запрос. Например, если Person это экземпляр класса, определенного PersonModel выше:

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

Это возвращает значение Name свойства для этого экземпляра PersonModel.

Настройка значений с помощью кодирования "ключ-значение"

Аналогичным образом SetValueForKey задайте значение для указанного ключа (как a NSString), относительно экземпляра класса KVC, получающего запрос. Поэтому снова, используя экземпляр PersonModel класса, как показано ниже:

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

Изменит значение Name свойства Jane Doeна .

Наблюдение за изменениями значений

С помощью наблюдения за ключом (KVO) можно подключить наблюдателя к определенному ключу соответствующего класса KVC и получать уведомления в любое время изменения значения этого ключа (с помощью методов KVC или непосредственного доступа к заданному свойству в коде C#). Например:

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

Теперь при Name изменении свойства экземпляра PersonPersonModel класса новое значение записывается в консоль.

Дополнительные сведения см. в руководстве по программированию на основе ключевых значений Apple.

Привязка данных

В следующих разделах показано, как использовать кодирование ключей и значение ключа для наблюдения за соответствующим классом для привязки данных к элементам пользовательского интерфейса в построителе интерфейсов Xcode вместо чтения и записи значений с помощью кода C#. Таким образом вы отделяете модель данных от представлений, которые используются для их отображения, что делает приложение Xamarin.Mac более гибким и удобным для обслуживания. Вы также значительно уменьшаете объем кода, который должен быть записан.

Определение модели данных

Прежде чем можно привязать элемент пользовательского интерфейса в Interface Builder, необходимо иметь класс соответствия KVC/KVO, определенный в приложении Xamarin.Mac, чтобы выступать в качестве модели данных для привязки. Модель данных предоставляет все данные, которые будут отображаться в пользовательском интерфейсе и получают любые изменения данных, которые пользователь делает в пользовательском интерфейсе при запуске приложения.

Например, если вы написали приложение, управляемое группой сотрудников, можно использовать следующий класс для определения модели данных:

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

Большинство функций этого класса были рассмотрены в разделе "Что такое кодирование с ключевым значением" выше. Однако давайте рассмотрим несколько конкретных элементов и некоторые дополнения, которые были сделаны, чтобы этот класс действовал как модель данных для контроллеров массивов и контроллеров деревьев (которые мы будем использовать позже для привязки представлений дерева данных, представлений структуры и представлений коллекции).

Во-первых, поскольку сотрудник может быть руководителем, мы использовали NSArray (в частности, так NSMutableArray , чтобы значения можно было изменить), чтобы разрешить сотрудникам, которым они удалось присоединиться к ним:

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

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

Две вещи, которые следует отметить здесь:

  1. Мы использовали NSMutableArray вместо стандартного массива или коллекции C#, так как это требование для привязки данных к элементам управления AppKit, таким как представления таблиц, представления структуры и коллекции.
  2. Мы предоставили массив сотрудников путем приведения его к NSArray объекту для привязки данных и изменили его форматированное имя C#, на тот, который ожидает привязка данных, Peopleв форме {class_name}Array (обратите внимание, personModelArray что первый символ был сделан нижним регистром).

Далее необходимо добавить некоторые специальные методы имени для поддержки контроллеров массивов и контроллеров деревьев:

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

Они позволяют контроллерам запрашивать и изменять отображаемые данные. Как и в приведенном NSArray выше примере, они имеют очень конкретное соглашение об именовании (которое отличается от типичных соглашений об именовании C#):

  • addObject: — добавляет объект в массив.
  • insertObject:in{class_name}ArrayAtIndex: — Где {class_name} находится имя класса. Этот метод вставляет объект в массив по заданному индексу.
  • removeObjectFrom{class_name}ArrayAtIndex: — Где {class_name} находится имя класса. Этот метод удаляет объект в массиве по заданному индексу.
  • set{class_name}Array: — Где {class_name} находится имя класса. Этот метод позволяет заменить существующий перенос новым.

В этих методах мы упаковали изменения в WillChangeValue массив и DidChangeValue сообщения для соответствия KVO.

Наконец, так как Icon свойство зависит от значения свойства, изменения isManager свойства могут не отражаться в Icon элементах пользовательского isManager интерфейса привязки данных (во время KVO):

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

Чтобы исправить это, мы используем следующий код:

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

Обратите внимание, что помимо собственного ключа, метод доступа также отправляет WillChangeValue и DidChangeValue сообщения для Icon ключа, isManager чтобы он также видел изменения.

Мы будем использовать PersonModel модель данных в остальной части этой статьи.

Простая привязка данных

Определим нашу модель данных, давайте рассмотрим простой пример привязки данных в построителе интерфейсов Xcode. Например, давайте добавим форму в наше приложение Xamarin.Mac, которое можно использовать для изменения PersonModel указанного выше. Мы добавим несколько текстовых полей и флажок для отображения и редактирования свойств нашей модели.

Сначала добавим новый контроллер представления в файл Main.storyboard в построителе интерфейсов и назовем его классSimpleViewController:

Adding a new view controller with a class named SimpleViewController.

Затем вернитесь к Visual Studio для Mac, измените файл SimpleViewController.cs (который был автоматически добавлен в наш проект) и предоформьте экземплярPersonModel, к которому мы будем привязать форму к данным. Добавьте следующий код:

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

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

Затем при загрузке представления создадим экземпляр нашего PersonModel кода и заполните его следующим кодом:

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;

}

Теперь нам нужно создать форму, дважды щелкните файл Main.storyboard , чтобы открыть его для редактирования в Конструкторе интерфейсов. Макет формы, чтобы выглядеть примерно так:

Editing the storyboard in Xcode

Чтобы привязать форму к PersonModel предоставленному ключу Person , сделайте следующее:

  1. Выберите текстовое поле имени сотрудника и перейдите в инспектор привязок.

  2. Установите флажок "Привязка" и выберите "Простой контроллер представления" в раскрывающемся списке. Затем введите self.Person.Name путь к ключу:

    Entering self dot person dot name for the Key Path.

  3. Выберите поле "Текст оккупации" и проверка поле "Привязка" и выберите "Простой контроллер представления" в раскрывающемся списке. Затем введите self.Person.Occupation путь к ключу:

    Entering self dot Person dot Occupation for the Key Path.

  4. Установите флажок "Сотрудник" и проверка поле "Привязка" и выберите "Простой контроллер представления" в раскрывающемся списке. Затем введите self.Person.isManager путь к ключу:

    Entering self dot Person dot isManager for the Key Path.

  5. Выберите поле "Число сотрудников", управляемое текстовое поле и проверка поле "Привязка к" и выберите "Простой контроллер представления" в раскрывающемся списке. Затем введите self.Person.NumberOfEmployees путь к ключу:

    Entering self dot Person dot NumberOfEmployees for the Key Path.

  6. Если сотрудник не является руководителем, мы хотим скрыть количество сотрудников, управляемых меткой и текстовым полем.

  7. Выберите число управляемых меток сотрудников, разверните скрытый откат и проверка поле "Привязка к" и выберите "Простой контроллер представления" в раскрывающемся списке. Затем введите self.Person.isManager путь к ключу:

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

  8. Выберите из раскрывающегося NSNegateBoolean списка "Преобразователь значений":

    Selecting the NSNegateBoolean key transformation

  9. Это сообщает привязке данных, что метка будет скрыта, если значение isManager свойства равно false.

  10. Повторите шаги 7 и 8 для числа управляемых текстовых полей сотрудников.

  11. Сохраните изменения и вернитесь к Visual Studio для Mac для синхронизации с Xcode.

При запуске приложения значения из Person свойства автоматически заполняются нашей формой:

Showing an auto-populated form

Все изменения, внесенные пользователями в форму, будут записаны обратно Person в свойство в контроллере представления. Например, отмена выбора "Сотрудник" — это менеджер , который обновляет Person экземпляр нашего PersonModel приложения, а число управляемых меток сотрудников и текстовое поле скрыты автоматически (с помощью привязки данных):

Hiding the number of employees for non-managers

Привязка данных представления таблицы

Теперь, когда у нас есть основы привязки данных, давайте рассмотрим более сложную задачу привязки данных с помощью контроллера массива и привязки данных к представлению таблицы. Дополнительные сведения о работе с представлениями таблиц см. в документации по представлениям таблиц.

Сначала добавим новый контроллер представления в файл Main.storyboard в построителе интерфейсов и назовем его классTableViewController:

Adding a new view controller with a class named TableViewController.

Затем давайте отредактируем файл TableViewController.cs (который был автоматически добавлен в наш проект) и предоставьте массивNSArray () PersonModel классов, к которым мы будем привязывать нашу форму. Добавьте следующий код:

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

Как и в PersonModel приведенном выше классе в разделе "Определение модели данных", мы предоставили четыре специально именованных открытых метода, чтобы контроллер массива и чтение и запись данных из нашей коллекции PersonModels.

Затем при загрузке представления необходимо заполнить наш массив этим кодом:

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

}

Теперь нам нужно создать представление таблицы, дважды щелкните файл Main.storyboard , чтобы открыть его для редактирования в Конструкторе интерфейсов. Макет таблицы, чтобы выглядеть примерно так:

Laying out a new table view

Необходимо добавить контроллер массива для предоставления привязанных данных к таблице, сделайте следующее:

  1. Перетащите контроллер массива из инспектора библиотеки в редактор интерфейса:

    Selecting an Array Controller from the Library

  2. Выберите контроллер массива в иерархии интерфейса и переключитесь на инспектор атрибутов:

    Selecting the Attributes Inspector

  3. Введите PersonModel имя класса, нажмите кнопку "Плюс" и добавьте три ключа. Присвойте им имя Nameи OccupationisManager:

    Adding the required key paths to the Object Controller.

  4. Это сообщает контроллеру массива, что он управляет массивом, и какие свойства он должен предоставлять (с помощью ключей).

  5. Перейдите в инспектор привязок и в разделе "Массив контента" выберите "Привязка" и "Контроллер представления таблиц". Введите путьself.personModelArrayк ключу модели:

    Entering a key path

  6. Это связывает контроллер массива PersonModels с массивом, который мы предоставили на контроллере представления.

Теперь необходимо привязать представление таблицы к контроллеру массива, сделайте следующее:

  1. Выберите представление таблицы и инспектор привязки:

    Selecting the Table View and Binding Inspector.

  2. В раскрывающемся списке содержимого выберите "Привязка" и "Контроллер массива". Введите arrangedObjects поле "Ключ контроллера":

    Defining the controller key

  3. Выберите ячейку представления таблицы в столбце Employee. В инспекторе привязок в раскрывающемся списке "Значение" выберите "Привязка к" и "Представление ячеек таблицы". Введите objectValue.Name путь к ключу модели:

    Setting the model key path for the Employee column.

  4. objectValue является текущим PersonModel в массиве, управляемым контроллером массива.

  5. Выберите ячейку представления таблицы в столбце "Оккупация". В инспекторе привязок в раскрывающемся списке "Значение" выберите "Привязка к" и "Представление ячеек таблицы". Введите objectValue.Occupation путь к ключу модели:

    Setting the model key path for the Occupation column.

  6. Сохраните изменения и вернитесь к Visual Studio для Mac для синхронизации с Xcode.

Если мы запускаем приложение, таблица будет заполнена нашим массивом PersonModels:

Running the application, which populates the array of PersonModels.

Привязка данных представления структуры

Привязка данных к представлению структуры очень похожа на привязку к представлению таблицы. Ключевое различие заключается в том, что мы будем использовать контроллер дерева вместо контроллера массива для предоставления привязанных данных к представлению структуры. Дополнительные сведения о работе с представлениями структуры см. в документации по представлениям структуры.

Сначала добавим новый контроллер представления в файл Main.storyboard в построителе интерфейсов и назовем его классOutlineViewController:

Adding a new view controller with a class named OutlineViewController.

Затем давайте отредактируем файл OutlineViewController.cs (который был автоматически добавлен в наш проект) и предоставьте массивNSArray () PersonModel классов, к которым мы будем привязывать нашу форму. Добавьте следующий код:

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

Как и в PersonModel приведенном выше классе в разделе "Определение модели данных", мы предоставили четыре специально именованных открытых метода, чтобы контроллер дерева и чтение и запись данных из нашей коллекции PersonModels.

Затем при загрузке представления необходимо заполнить наш массив этим кодом:

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

}

Теперь нам нужно создать представление структуры, дважды щелкните файл Main.storyboard , чтобы открыть его для редактирования в Конструкторе интерфейсов. Макет таблицы, чтобы выглядеть примерно так:

Creating the outline view

Необходимо добавить контроллер дерева для предоставления привязанных данных в структуру, сделайте следующее:

  1. Перетащите контроллер дерева из инспектора библиотеки в редактор интерфейса:

    Selecting a Tree Controller from the Library

  2. Выберите контроллер дерева в иерархии интерфейса и переключитесь на инспектор атрибутов:

    Selecting the Attribute Inspector

  3. Введите PersonModel имя класса, нажмите кнопку "Плюс" и добавьте три ключа. Присвойте им имя Nameи OccupationisManager:

    Adding the required key paths for PersonModel.

  4. Это сообщает контроллеру дерева, что он управляет массивом, и какие свойства он должен предоставлять (с помощью ключей).

  5. В разделе "Контроллер дерева" введите "Дочерние", введите NumberOfEmployeespersonModelArray в поле "Число" и введите isEmployee в поле "Лист":

    Setting the Tree Controller key paths

  6. Это указывает контроллеру дерева, где найти все дочерние узлы, сколько дочерних узлов есть и если текущий узел имеет дочерние узлы.

  7. Перейдите в инспектор привязок и в разделе "Массив контента" выберите "Привязка" и "Владелец файла". Введите путьself.personModelArrayк ключу модели:

    Editing the key path

  8. Это связывает контроллер дерева с массивом PersonModels , который мы предоставили на контроллере представления.

Теперь необходимо привязать представление структуры к контроллеру дерева, сделайте следующее:

  1. Выберите представление структуры и в инспекторе привязки выберите:

    Selecting the Outline View and Binding Inspector.

  2. В раскрывающемся списке "Содержимое представления структуры" выберите "Привязка" и "Контроллер дерева". Введите arrangedObjects поле "Ключ контроллера":

    Setting the controller key

  3. Выберите ячейку представления таблицы в столбце Employee. В инспекторе привязок в раскрывающемся списке "Значение" выберите "Привязка к" и "Представление ячеек таблицы". Введите objectValue.Name путь к ключу модели:

    Entering the model key path value objectValue dot Name.

  4. objectValue — текущий PersonModel в массиве, управляемый контроллером дерева.

  5. Выберите ячейку представления таблицы в столбце "Оккупация". В инспекторе привязок в раскрывающемся списке "Значение" выберите "Привязка к" и "Представление ячеек таблицы". Введите objectValue.Occupation путь к ключу модели:

    Entering the model key path value objectValue dot Occupation.

  6. Сохраните изменения и вернитесь к Visual Studio для Mac для синхронизации с Xcode.

Если мы запускаем приложение, структура будет заполнена нашим массивом PersonModels:

Running the application, which populates our array of PersonModels.

Привязка данных представления коллекции

Привязка данных с представлением коллекции очень похожа на привязку с представлением таблицы, так как контроллер массива используется для предоставления данных для коллекции. Так как представление коллекции не имеет предустановленного формата отображения, для получения отзывов о взаимодействии с пользователем и отслеживания выбора пользователей требуется дополнительная работа.

Внимание

Из-за проблемы в Xcode 7 и macOS 10.11 (и больше), представления коллекции не могут использоваться внутри файлов раскадровки (раскадровки). В результате необходимо продолжать использовать XIB-файлы для определения представлений коллекции для приложений Xamarin.Mac. Дополнительные сведения см. в документации по представлениям коллекции.

Отладка собственных сбоев

Ошибка при привязках данных может привести к сбою машинного кода в неуправляемом коде и привести к сбою приложения Xamarin.Mac с ошибкой SIGABRT :

Example of a native crash dialog box

Обычно во время привязки данных во время привязки данных возникают четыре основных причины сбоя в машинном коде:

  1. Модель данных не наследует от NSObject подкласса NSObject.
  2. Свойство не было предоставлено Objective-C для использования атрибута [Export("key-name")] .
  3. Вы не заключили изменения в значение метода доступа и WillChangeValue вызовы метода (указывая тот же ключ, что Export и DidChangeValue атрибут).
  4. У вас есть неправильный или неправильный ключ в инспекторе привязки в построителе интерфейсов.

Декодирование сбоя

Давайте создадим собственный сбой в привязке данных, чтобы мы могли показать, как найти и исправить его. В построителе интерфейсов мы изменим привязку первой метки в примере представления коллекции на NameTitle:

Editing the binding key

Давайте сохраните изменение, переключимся на Visual Studio для Mac, чтобы синхронизироваться с Xcode и запустить наше приложение. При отображении представления коллекции приложение мгновенно завершает работу с SIGABRT ошибкой (как показано в выходных данных приложения в Visual Studio для Mac), так как PersonModel свойство с ключом Titleне предоставляется:

Example of a binding error

Если прокручиваться до самой верхней части ошибки в выходных данных приложения, мы видим ключ для решения проблемы:

Finding the issue in the error log

Эта строка говорит нам, что ключ Title не существует в объекте, к которому мы привязываются. Если изменить привязку обратно Name в конструктор интерфейсов, сохранить, синхронизировать, перестроить и запустить, приложение будет работать должным образом без проблем.

Итоги

В этой статье подробно описано, как работать с привязкой данных и кодом ключа в приложении Xamarin.Mac. Во-первых, он рассмотрел возможность предоставления класса Objective-C C# использованию кода ключевого значения (KVC) и наблюдения за ключом (KVO). Далее он показал, как использовать класс, совместимый с KVO, и Привязка данных к элементам пользовательского интерфейса в построителе интерфейсов Xcode. Наконец, она показала сложную привязку данных с помощью контроллеров массива и контроллеров деревьев.