Xamarin.Mac 中的数据绑定和键值编码
本文介绍如何使用键值编码和键值观察来允许将数据绑定到 Xcode 接口生成器中的 UI 元素。
概述
在 Xamarin.Mac 应用程序中使用 C# 和 .NET 时,可以访问开发人员处理Objective-C和 Xcode 时使用的相同键值编码和数据绑定技术。 由于 Xamarin.Mac 直接与 Xcode 集成,因此可以使用 Xcode 的 Interface Builder 将数据绑定到 UI 元素,而不是编写代码。
通过在 Xamarin.Mac 应用程序中使用键值编码和数据绑定技术,可以大大减少编写和维护的代码量,以填充和使用 UI 元素。 还可以从前端用户界面(Model-View-Controller)进一步分离支持数据(数据模型),从而更轻松地维护、更灵活的应用程序设计。
在本文中,我们将介绍在 Xamarin.Mac 应用程序中使用键值编码和数据绑定的基础知识。 强烈建议先浏览 Hello、Mac 文章,特别是 Xcode 和 Interface Builder 和输出口和 操作 简介部分,因为它介绍本文中将要使用的关键概念和技术。
你可能还需要查看 Xamarin.Mac Internals 文档的“公开 C# 类/方法Objective-C”部分,其中还介绍了Export
Register
用于将 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
则为类属性的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
返回指定 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; }
}
此处要注意的两件事:
- 我们使用了标准
NSMutableArray
C# 数组或集合,因为这是绑定到 AppKit 控件(如表视图、大纲视图和集合)的要求。 - 我们公开了员工数组,方法是将其强制转换为
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
访问器还会发送WillChangeValue
Icon
密钥和DidChangeValue
消息,这样它也会看到更改。
我们将在本文中的其余部分使用 PersonModel
数据模型。
简单数据绑定
定义数据模型后,让我们看看 Xcode 接口生成器中的数据绑定的简单示例。 例如,让我们将窗体添加到 Xamarin.Mac 应用程序,该应用程序可用于编辑 PersonModel
上面定义的表单。 我们将添加一些文本字段和一个复选框来显示和编辑模型的属性。
首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类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 中编辑。 将窗体布局为如下所示:
若要将数据绑定到通过Person
密钥公开的窗体PersonModel
,请执行以下操作:
选择 “员工姓名 文本”字段并切换到“ 绑定检查器”。
选中“ 绑定到 ”框,然后从下拉列表中选择 “简单视图控制器 ”。
self.Person.Name
接下来输入密钥路径:选择“职业文本字段”并检查“绑定到”框,然后从下拉列表中选择“简单视图控制器”。
self.Person.Occupation
接下来输入密钥路径:选择员工是经理复选框,检查“绑定到”框,然后从下拉列表中选择“简单视图控制器”。
self.Person.isManager
接下来输入密钥路径:选择“员工托管文本字段数”,检查“绑定到”框,然后从下拉列表中选择“简单视图控制器”。
self.Person.NumberOfEmployees
接下来输入密钥路径:如果员工不是经理,我们希望隐藏员工托管标签和文本字段的数量。
选择“员工托管标签数”,展开“隐藏”关闭,检查“绑定到”框,然后从下拉列表中选择“简单视图控制器”。
self.Person.isManager
接下来输入密钥路径:从“值转换器”下拉列表中选择
NSNegateBoolean
:这会告知数据绑定,如果属性的值
isManager
为false
,标签将被隐藏。对 “员工托管 文本”字段的数量重复步骤 7 和 8。
保存更改并返回到 Visual Studio for Mac 以与 Xcode 同步。
如果运行应用程序,则属性中的值 Person
将自动填充表单:
用户对窗体所做的任何更改都将写回到 Person
视图控制器中的属性。 例如,取消选择 员工是经理 更新 Person
我们的 PersonModel
实例,员工 托管 标签和文本字段的数量会自动隐藏(通过数据绑定):
表视图数据绑定
现在,我们已经有了数据绑定的基础知识,接下来让我们通过对表视图使用 数组控制器 和数据绑定来查看更复杂的数据绑定任务。 有关使用表视图的详细信息,请参阅表 视图 文档。
首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类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 中编辑。 布局表格,如下所示:
我们需要添加数组 控制器 来向表提供绑定数据,请执行以下操作:
将数组控制器从库检查器拖到接口编辑器上:
在接口层次结构中选择数组控制器并切换到属性检查器:
输入
PersonModel
类 名,单击 加号 按钮并添加三个键。 命名它们Name
,Occupation
并isManager
:这告诉数组控制器它正在管理数组,以及它应公开哪些属性(通过密钥)。
切换到“绑定检查器”,然后在“内容数组”下选择“绑定到”和“表视图控制器”。 输入模型密钥路径:
self.personModelArray
这会将数组控制器绑定到我们在视图控制器上公开的
PersonModels
数组。
现在,我们需要将表视图绑定到数组控制器,请执行以下操作:
选择表视图和 绑定检查器:
在“表内容关闭”下,选择“绑定到”和“数组控制器”。 输入
arrangedObjects
控制器密钥字段:选择“员工”列下的“表视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”和“表单元格视图”。 输入
objectValue.Name
模型密钥路径:objectValue
是由阵列控制器管理的数组中的当前PersonModel
值。选择“职业”列下的“表格视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”和“表单元格视图”。 输入
objectValue.Occupation
模型密钥路径:保存更改并返回到 Visual Studio for Mac 以与 Xcode 同步。
如果运行应用程序,则表将填充以下数组 PersonModels
:
大纲视图数据绑定
针对大纲视图的数据绑定与针对表视图的绑定非常相似。 主要区别在于,我们将使用树控制器而不是数组控制器向大纲视图提供绑定数据。 有关使用大纲视图的详细信息,请参阅大纲 视图 文档。
首先,让我们在 Interface Builder 中向 Main.storyboard 文件添加新的视图控制器,并将其类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 中编辑。 布局表格,如下所示:
我们需要添加 树控制器 来向大纲提供绑定数据,请执行以下操作:
将树控制器从库检查器拖到接口编辑器上:
在接口层次结构中选择树控制器并切换到属性检查器:
输入
PersonModel
类 名,单击 加号 按钮并添加三个键。 命名它们Name
,Occupation
并isManager
:这告诉树控制器它管理的是一个数组,以及它应该公开哪些属性(通过密钥)。
在“树控制器”部分下,输入
personModelArray
“儿童”,在“计数”下输入NumberOfEmployees
,并在“叶”下输入isEmployee
:这会告知树控制器在何处查找任何子节点、存在多少个子节点以及当前节点是否具有子节点。
切换到“绑定检查器”,然后在“内容数组”下选择“绑定到”和“文件所有者”。 输入模型密钥路径:
self.personModelArray
这会将树控制器绑定到我们在视图控制器上公开的
PersonModels
树控制器数组。
现在,我们需要将大纲视图绑定到树控制器,请执行以下操作:
选择大纲视图并在 绑定检查器 中选择:
在大纲视图内容关闭下,选择“绑定到”和“树控制器”。 输入
arrangedObjects
控制器密钥字段:选择“员工”列下的“表视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”和“表单元格视图”。 输入
objectValue.Name
模型密钥路径:objectValue
是由树控制器管理的数组中的当前PersonModel
值。选择“职业”列下的“表格视图单元格”。 在“值关闭”下的“绑定检查器”中,选择“绑定到”和“表单元格视图”。 输入
objectValue.Occupation
模型密钥路径:保存更改并返回到 Visual Studio for Mac 以与 Xcode 同步。
如果运行应用程序,大纲将填充以下数组 PersonModels
:
集合视图数据绑定
使用集合视图进行数据绑定与表视图非常类似,因为数组控制器用于为集合提供数据。 由于集合视图没有预设的显示格式,因此需要执行更多工作来提供用户交互反馈和跟踪用户选择。
重要
由于 Xcode 7 和 macOS 10.11(及更高)中存在问题,集合视图无法在情节提要(.storyboard)文件中使用。 因此,需要继续使用 .xib 文件为 Xamarin.Mac 应用定义集合视图。 有关详细信息,请参阅我们的 集合视图 文档。
调试本机崩溃
在数据绑定中出错可能会导致非托管代码中的本机崩溃,并导致 Xamarin.Mac 应用程序完全失败并出现SIGABRT
错误:
在数据绑定期间,本机崩溃通常有四个主要原因:
- 数据模型不继承自
NSObject
或子NSObject
类 。 - 未将属性公开为 Objective-C 使用
[Export("key-name")]
属性。 - 未包装对访问器值的
WillChangeValue
更改和DidChangeValue
方法调用(指定与属性相同的键Export
)。 - 接口生成器中的 绑定检查器 中有错误或错误键入的键。
解码崩溃
让我们在数据绑定中导致本机崩溃,以便我们可以演示如何查找和修复它。 在 Interface Builder 中,让我们在集合视图示例中将第一个标签的绑定更改为Name
Title
:
保存更改,切换回 Visual Studio for Mac 以与 Xcode 同步并运行应用程序。 显示“集合视图”时,应用程序将立即崩溃并SIGABRT
出现错误(如 Visual Studio for Mac 中的应用程序输出所示),因为PersonModel
该属性未使用键Title
公开:
如果滚动到应用程序输出中错误顶部,我们可以看到解决问题的关键:
此行告诉我们,我们绑定到的对象上不存在键 Title
。 如果将绑定更改回 Name
Interface Builder 中,保存、同步、重新生成和运行,应用程序将按预期运行,而不会出现问题。
总结
本文详细介绍了如何使用 Xamarin.Mac 应用程序中的数据绑定和键值编码。 首先,它考虑了如何使用键值编码(KVC)和键值观察(KVO)向其公开 C# 类 Objective-C 。 接下来,它演示了如何使用符合 KVO 的类和数据将其绑定到 Xcode 接口生成器中的 UI 元素。 最后,它显示了使用数组控制器和树控制器的复杂数据绑定。