Xamarin.Mac 中的数据绑定和键值编码

本文介绍如何使用键值编码和键值观察来允许将数据绑定到 Xcode 接口生成器中的 UI 元素。

概述

在 Xamarin.Mac 应用程序中使用 C# 和 .NET 时,可以访问开发人员处理Objective-C和 Xcode 时使用的相同键值编码和数据绑定技术。 由于 Xamarin.Mac 直接与 Xcode 集成,因此可以使用 Xcode 的 Interface Builder 将数据绑定到 UI 元素,而不是编写代码。

通过在 Xamarin.Mac 应用程序中使用键值编码和数据绑定技术,可以大大减少编写和维护的代码量,以填充和使用 UI 元素。 还可以从前端用户界面(Model-View-Controller)进一步分离支持数据(数据模型),从而更轻松地维护、更灵活的应用程序设计。

An example of the running app

在本文中,我们将介绍在 Xamarin.Mac 应用程序中使用键值编码和数据绑定的基础知识。 强烈建议先浏览 Hello、Mac 文章,特别是 Xcode 和 Interface Builder 和输出口和 操作 简介部分,因为它介绍本文中将要使用的关键概念和技术。

你可能还需要查看 Xamarin.Mac Internals 文档的“公开 C# 类/方法Objective-C”部分,其中还介绍了ExportRegister用于将 C# 类与Objective-C对象和 UI 元素连接起来的特性。

什么是键值编码

键值编码(KVC)是间接访问对象的属性的机制,它使用键(特殊格式的字符串)来标识属性,而不是通过实例变量或访问器方法get/set()访问它们。 通过在 Xamarin.Mac 应用程序中实现符合键值编码的访问器,可以访问其他 macOS(以前称为 OS X)功能,例如键值观察(KVO)、数据绑定、核心数据、Cocoa 绑定和可脚本性。

通过在 Xamarin.Mac 应用程序中使用键值编码和数据绑定技术,可以大大减少编写和维护的代码量,以填充和使用 UI 元素。 还可以从前端用户界面(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 技术访问属性。

最后,为了能够成为属性值的键值观察更改,访问器必须包装对其值所做的更改 WillChangeValue 以及 DidChangeValue 方法调用(指定与属性相同的键 Export )。 例如:

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

此步骤对于 Xcode 接口生成器中的数据绑定非常重要(本文稍后将介绍)。

有关详细信息,请参阅 Apple 的 键值编码编程指南

密钥和密钥路径

是标识对象的特定属性的字符串。 通常,键对应于键值编码兼容对象中的访问器方法的名称。 键必须使用 ASCII 编码,通常以小写字母开头,并且可能不包含空格。 因此,如上例所示,Name则为类属性的NamePersonModel键值。 它们公开的属性的键和名称不必相同,但在大多数情况下,它们都是。

键路径是一个点分隔键的字符串,用于指定要遍历的对象属性的层次结构。 序列中第一个键的属性相对于接收方,并且每个后续键相对于上一个属性的值进行评估。 同样,使用点表示法遍历 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 返回指定 Key(作为 a NSString),相对于接收请求的 KVC 类的实例的值。 例如,如果是 Person 上面定义的类的 PersonModel 实例:

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

这将返回该实例PersonModel的属性Name的值。

使用键值编码设置值

同样,相对于 SetValueForKey 接收请求的 KVC 类的实例,为指定的键(作为 a NSString)设置值。 因此,再次使用类的 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修改类实例PersonModel的属性Person时,新值将写入控制台。

有关详细信息,请参阅 Apple 的 键值观察编程指南简介。

数据绑定

以下部分将演示如何使用键值编码和键值观察合规类将数据绑定到 Xcode 接口生成器中的 UI 元素,而不是使用 C# 代码读取和写入值。 这样,就可以将数据模型与用于显示它们的视图分开,使 Xamarin.Mac 应用程序更加灵活且更易于维护。 此外,还可以大幅减少必须写入的代码量。

定义数据模型

在 Interface Builder 中将数据绑定 UI 元素之前,必须在 Xamarin.Mac 应用程序中定义符合 KVC/KVO 的类才能充当 绑定的数据模型 。 数据模型提供将在用户界面中显示的所有数据,并接收用户在运行应用程序时在 UI 中所做的任何修改。

例如,如果要编写托管一组员工的应用程序,则可以使用以下类来定义数据模型:

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数据绑定所需的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 ,因此对属性的更改 isManager 可能不会反映在数据绑定 UI 元素中 Icon (在 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");
    }
}

请注意,除了自己的密钥外,isManager访问器还会发送WillChangeValueIcon密钥和DidChangeValue消息,这样它也会看到更改。

我们将在本文中的其余部分使用 PersonModel 数据模型。

简单数据绑定

定义数据模型后,让我们看看 Xcode 接口生成器中的数据绑定的简单示例。 例如,让我们将窗体添加到 Xamarin.Mac 应用程序,该应用程序可用于编辑 PersonModel 上面定义的表单。 我们将添加一些文本字段和一个复选框来显示和编辑模型的属性。

首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类SimpleViewController命名为:

Adding a new view controller with a class named SimpleViewController.

接下来,返回到 Visual Studio for 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 文件以在 Interface Builder 中编辑。 将窗体布局为如下所示:

Editing the storyboard in Xcode

若要将数据绑定到通过Person密钥公开的窗体PersonModel,请执行以下操作:

  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. 这会告知数据绑定,如果属性的值 isManagerfalse,标签将被隐藏。

  10. “员工托管 文本”字段的数量重复步骤 7 和 8。

  11. 保存更改并返回到 Visual Studio for Mac 以与 Xcode 同步。

如果运行应用程序,则属性中的值 Person 将自动填充表单:

Showing an auto-populated form

用户对窗体所做的任何更改都将写回到 Person 视图控制器中的属性。 例如,取消选择 员工是经理 更新 Person 我们的 PersonModel 实例,员工 托管 标签和文本字段的数量会自动隐藏(通过数据绑定):

Hiding the number of employees for non-managers

表视图数据绑定

现在,我们已经有了数据绑定的基础知识,接下来让我们通过对表视图使用 数组控制器 和数据绑定来查看更复杂的数据绑定任务。 有关使用表视图的详细信息,请参阅表 视图 文档。

首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类TableViewController命名为:

Adding a new view controller with a class named TableViewController.

接下来,让我们编辑TableViewController.cs文件(已自动添加到项目),并公开将表单绑定到的数据类的PersonModel数组(NSArray)。 添加以下代码:

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 文件将其打开,以便在 Interface Builder 中编辑。 布局表格,如下所示:

Laying out a new table view

我们需要添加数组 控制器 来向表提供绑定数据,请执行以下操作:

  1. 数组控制器从库检查器拖到接口编辑器上:

    Selecting an Array Controller from the Library

  2. 接口层次结构中选择数组控制器并切换到属性检查器

    Selecting the Attributes Inspector

  3. 输入 PersonModel,单击 加号 按钮并添加三个键。 命名它们 NameOccupationisManager

    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. 选择“员工”列下的“表视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”表单元格视图”。 输入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 for Mac 以与 Xcode 同步。

如果运行应用程序,则表将填充以下数组 PersonModels

Running the application, which populates the array of PersonModels.

大纲视图数据绑定

针对大纲视图的数据绑定与针对表视图的绑定非常相似。 主要区别在于,我们将使用树控制器而不是数组控制器向大纲视图提供绑定数据。 有关使用大纲视图的详细信息,请参阅大纲 视图 文档。

首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类OutlineViewController命名为:

Adding a new view controller with a class named OutlineViewController.

接下来,编辑OutlineViewController.cs文件(已自动添加到项目),并公开将表单绑定到的数据类的PersonModel数组(NSArray)。 添加以下代码:

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 文件将其打开,以便在 Interface Builder 中编辑。 布局表格,如下所示:

Creating the outline view

我们需要添加 树控制器 来向大纲提供绑定数据,请执行以下操作:

  1. 树控制器从库检查器拖到接口编辑器上:

    Selecting a Tree Controller from the Library

  2. 接口层次结构中选择树控制器并切换到属性检查器

    Selecting the Attribute Inspector

  3. 输入 PersonModel,单击 加号 按钮并添加三个键。 命名它们 NameOccupationisManager

    Adding the required key paths for PersonModel.

  4. 这告诉树控制器它管理的是一个数组,以及它应该公开哪些属性(通过密钥)。

  5. “树控制器”部分下,输入personModelArray儿童”,在“计数下输入NumberOfEmployees,并在“叶”下输入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. 选择“员工”列下的“表视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”表单元格视图”。 输入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 for Mac 以与 Xcode 同步。

如果运行应用程序,大纲将填充以下数组 PersonModels

Running the application, which populates our array of PersonModels.

集合视图数据绑定

使用集合视图进行数据绑定与表视图非常类似,因为数组控制器用于为集合提供数据。 由于集合视图没有预设的显示格式,因此需要执行更多工作来提供用户交互反馈和跟踪用户选择。

重要

由于 Xcode 7 和 macOS 10.11(及更高)中存在问题,集合视图无法在情节提要(.storyboard)文件中使用。 因此,需要继续使用 .xib 文件为 Xamarin.Mac 应用定义集合视图。 有关详细信息,请参阅我们的 集合视图 文档。

调试本机崩溃

在数据绑定中出错可能会导致非托管代码中的本机崩溃,并导致 Xamarin.Mac 应用程序完全失败并出现SIGABRT错误:

Example of a native crash dialog box

在数据绑定期间,本机崩溃通常有四个主要原因:

  1. 数据模型不继承自 NSObject 或子 NSObject类 。
  2. 未将属性公开为 Objective-C 使用 [Export("key-name")] 属性。
  3. 未包装对访问器值的 WillChangeValue 更改和 DidChangeValue 方法调用(指定与属性相同的键 Export )。
  4. 接口生成器中的 绑定检查器 中有错误或错误键入的键。

解码崩溃

让我们在数据绑定中导致本机崩溃,以便我们可以演示如何查找和修复它。 在 Interface Builder 中,让我们在集合视图示例中将第一个标签的绑定更改为NameTitle

Editing the binding key

保存更改,切换回 Visual Studio for Mac 以与 Xcode 同步并运行应用程序。 显示“集合视图”时,应用程序将立即崩溃并SIGABRT出现错误(如 Visual Studio for Mac 中的应用程序输出所示),因为PersonModel该属性未使用键Title公开:

Example of a binding error

如果滚动到应用程序输出错误顶部,我们可以看到解决问题的关键:

Finding the issue in the error log

此行告诉我们,我们绑定到的对象上不存在键 Title 。 如果将绑定更改回 Name Interface Builder 中,保存、同步、重新生成和运行,应用程序将按预期运行,而不会出现问题。

总结

本文详细介绍了如何使用 Xamarin.Mac 应用程序中的数据绑定和键值编码。 首先,它考虑了如何使用键值编码(KVC)和键值观察(KVO)向其公开 C# 类 Objective-C 。 接下来,它演示了如何使用符合 KVO 的类和数据将其绑定到 Xcode 接口生成器中的 UI 元素。 最后,它显示了使用数组控制器和树控制器的复杂数据绑定。