Xamarin.Mac 中的數據系結和索引鍵/值編碼
本文涵蓋使用索引鍵/值編碼和索引鍵/值觀察,以允許將數據系結至 Xcode 介面產生器中的 UI 元素。
概觀
在 Xamarin.Mac 應用程式中使用 C# 和 .NET 時,您可以存取開發人員在 和 Xcode 中Objective-C運作的相同機碼值編碼和數據系結技術。 由於 Xamarin.Mac 直接與 Xcode 整合,因此您可以使用 Xcode 的 介面產生器 將數據系結至 UI 元素,而不是撰寫程式代碼。
藉由在 Xamarin.Mac 應用程式中使用索引鍵/值編碼和數據系結技術,您可以大幅減少您必須撰寫和維護的程式碼數量,以填入和使用 UI 元素。 您也可以從前端使用者介面(Model-View-Controller)進一步分離備份數據(數據模型),進而更輕鬆地維護、更有彈性的應用程式設計。
在本文中,我們將討論在 Xamarin.Mac 應用程式中使用索引鍵/值編碼和數據系結的基本概念。 強烈建議您先完成 Hello,Mac 文章,特別是 Xcode 和 Interface Builder 和 Outlets 和 Actions 簡介小節,因為它涵蓋我們將在本文中使用的重要概念和技術。
您可能也想要查看 Xamarin.Mac Internals 檔的公開 C# 類別/方法Objective-C一節,它也會說明 Register
用來將 C# 類別連線至Objective-C物件和 UI 元素的 和 Export
屬性。
什麼是索引鍵/值編碼
索引鍵/值編碼 (KVC) 是間接存取物件屬性的機制,使用索引鍵(特別格式化的字串)來識別屬性,而不是透過實例變數或存取子方法存取它們。get/set
藉由在 Xamarin.Mac 應用程式中實作索引鍵/值編碼相容存取子,您可以存取其他 macOS(先前稱為 OS X)功能,例如索引鍵/值觀察(KVO)、數據系結、Core Data、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 技術存取屬性。
最後,若要能夠對屬性的值進行Key-Value Observed變更,存取子必須在和 DidChangeValue
方法呼叫中WillChangeValue
包裝其值的變更(指定與 Export
屬性相同的Key)。 例如:
set {
WillChangeValue ("Name");
_name = value;
DidChangeValue ("Name");
}
此步驟對於 Xcode 介面產生器中的數據系結非常重要 (如本文稍後所見)。
如需詳細資訊,請參閱Apple的 索引鍵/值編碼程序設計指南。
索引鍵和金鑰路徑
Key 是識別物件特定屬性的字串。 一般而言,索引鍵會對應至索引鍵值編碼相容物件中存取子方法的名稱。 索引鍵必須使用 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 的值(做為 ), NSString
相對於接收要求的 KVC 類別實例。 例如,如果 Person
是上面定義的類別實例 PersonModel
:
// Read value
var name = Person.ValueForKey (new NSString("Name"));
這會傳回該 實例PersonModel
的 Name
屬性值。
使用機碼/值編碼設定值
同樣地,相對於 SetValueForKey
接收要求的 KVC 類別實例,設定指定之 Key (as 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
修改 類別實例的 Person
PersonModel
屬性時,就會將新值寫出主控台。
如需詳細資訊,請參閱Apple的 索引鍵/值觀察程序設計指南簡介。
資料繫結
下列各節將示範如何使用索引鍵/值編碼和索引鍵/值觀察相容類別,將數據系結至 Xcode 介面產生器中的 UI 元素,而不是使用 C# 程式代碼來讀取和寫入值。 如此一來,您就可以將數據模型與用來顯示它們的檢視區隔開,讓 Xamarin.Mac 應用程式更有彈性且更容易維護。 您也大幅減少必須撰寫的程式代碼數量。
定義您的數據模型
您必須先在 Xamarin.Mac 應用程式中定義 KVC/KVO 相容類別,才能在介面產生器中系結 UI 元素,才能做為 系結的數據模型 。 數據模型會提供使用者介面中顯示的所有數據,並在執行應用程式時,接收使用者對 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
變更為數據系結所預期的personModelArray
數位,格式 為 {class_name}Array (請注意,第一個字元已做為小寫)。
接下來,我們需要新增一些特別命名的公用方法,以支援 數位控制器 和 樹狀目錄控制器:
[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");
}
}
請注意,除了自己的 Key 之外,存取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 中編輯它。 將表單設定為如下所示:
若要將數據系結至 PersonModel
透過密鑰公開的 Person
表單,請執行下列動作:
選取 [ 員工名稱 文字] 字段,然後切換至 [ 系結偵測器]。
核取 [ 系結至] 方塊,然後從下拉式清單中選取 [ 簡單檢視控制器 ]。 下一步輸入
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
控制器中的屬性。 例如,取消選取 Employee 是經理 更新 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 檔案以開啟它以在介面產生器中編輯。 配置數據表,看起來如下:
我們需要新增 數位控制器 ,以將系結數據提供給數據表,請執行下列動作:
將數位控制器從連結庫偵測器拖曳至介面編輯器:
在 [介面階層] 中選取 [陣列控制器],然後切換至 [屬性偵測器]:
針對 [類別名稱] 輸入
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 檔案,以開啟它以在介面產生器中編輯。 配置數據表,看起來如下:
我們需要新增 樹狀控制器 ,以提供系結數據至大綱,請執行下列動作:
將樹狀結構控制器從連結庫偵測器拖曳至介面編輯器:
選取 [介面階層] 中的 [樹狀控制器],然後切換至 [屬性偵測器]:
針對 [類別名稱] 輸入
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 應用程式的集合檢視。 如需詳細資訊,請參閱我們的 集合檢視 檔。
偵錯原生當機
在數據系結中犯錯可能會導致 Unmanaged 程式代碼中的原生損毀 ,並導致 Xamarin.Mac 應用程式完全失敗併發生 SIGABRT
錯誤:
在數據系結期間,原生當機通常有四個主要原因:
- 您的數據模型不會繼承自
NSObject
或的NSObject
子類別。 - 您未使用
[Export("key-name")]
屬性公開Objective-C屬性。 - 您在 和
DidChangeValue
方法呼叫中WillChangeValue
未包裝存取子值的變更(指定與Export
屬性相同的索引鍵)。 - 您在介面產生器中的 系結偵測器 中有錯誤或錯誤的索引鍵。
譯碼當機
讓我們在數據系結中造成原生損毀,以便示範如何找出並修正它。 在介面產生器中,讓我們將集合檢視範例中第一個標籤的系結從 Name
變更為 Title
:
讓我們儲存變更,切換回 Visual Studio for Mac 以與 Xcode 同步並執行應用程式。 顯示集合檢視時,應用程式會暫時當機並SIGABRT
出現錯誤(如 Visual Studio for Mac 中的應用程式輸出所示),因為 PersonModel
不會公開具有 Key Title
的屬性:
如果我們捲動至應用程式輸出中錯誤頂端,我們可以看到解決問題的關鍵:
這一行告訴我們,我們系結的對象上沒有索引鍵 Title
。 如果我們在 Interface Builder 中將系結變更回 Name
,請儲存、同步、重建並執行,應用程式將會如預期般執行,而不會發生問題。
摘要
本文已詳細探討在 Xamarin.Mac 應用程式中使用數據系結和索引鍵/值編碼。 首先,它會使用索引鍵/值編碼 (KVC) 和索引鍵/值觀察 (KVO) 將 C# 類別公開至 Objective-C 。 接下來,它示範如何使用 KVO 相容類別,並將它系結至 Xcode 介面產生器中的 UI 元素。 最後,它會顯示使用 數位控制器 和 樹狀目錄控制器的複雜數據系結。