Xamarin.Mac でのデータ バインディングとキー値のコーディング

この記事では、Xcode の Interface Builder で UI 要素へのデータ バインディングを可能にするために、キー値のコーディングとキー値の監視の使用について説明します。

概要

Xamarin.Mac アプリケーションで C# と .NET を使用する場合、開発者と Xcode が行うのと同じキー値コーディングとデータ バインディング手法にObjective-Cアクセスできます。 Xamarin.Mac は Xcode と直接統合されるため、コードを記述する代わりに、Xcode の インターフェイス ビルダー を使用して UI 要素を使用してデータ バインドを行うことができます。

Xamarin.Mac アプリケーションでキー値のコーディングとデータ バインディングの手法を使用すると、UI 要素を設定して操作するために必要なコードの量を大幅に減メインできます。 また、バッキング データ (データ モデル) をフロントエンド ユーザー インターフェイス (Model-View-Controller) からさらに切り離すことで、アプリケーション設計のメインメインしやすくなるという利点もあります。

An example of the running app

この記事では、Xamarin.Mac アプリケーションでのキー値のコーディングとデータ バインディングの操作の基本について説明します。 この記事で使用する 主要な概念と手法については、まず Hello Mac の記事、特 に Xcode とインターフェイス ビルダーアウトレットとアクション の概要に関するセクションを参照することを強くお勧めします。

Xamarin.Mac Internals ドキュメントの「C# クラス/メソッドを Xamarin.Mac Internals のセクションにObjective-C公開する」も参照してください。C# クラスObjective-Cをオブジェクトと UI 要素に結び付けるために使用される属性についてもExport説明Registerしています。

キー値のコーディングとは

キー値コーディング (KVC) は、インスタンス変数またはアクセサー メソッド () を介してプロパティにアクセスするのではなく、キー (特別に書式設定された文字列) を使用してプロパティを識別して、オブジェクトのプロパティに間接的にアクセスするためのメカニズムですget/set。 Xamarin.Mac アプリケーションでキー値コーディングに準拠したアクセサーを実装することで、キー値監視 (KVO)、データ バインディング、コア データ、Cocoa バインディング、スクリプト機能など、他の macOS (旧称 OS X) 機能にアクセスできます。

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 値を定義します。

最後に、プロパティの値に対する Key-Value Observed の変更を行うことができるようにするには、アクセサーは値の変更をラップして WillChangeValue メソッド呼び出しを行う必要があります (属性と DidChangeValue 同じキー Export を指定します)。 次に例を示します。

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

この手順は、 Xcode のインターフェイス ビルダーのデータ バインディングに非常 に重要です (この記事の後半で説明します)。

詳細については、Apple の キー値コーディング プログラミング ガイドを参照してください。

キーとキー パス

Key は、オブジェクトの特定のプロパティを識別する文字列です。 通常、キーは、キー値コーディングに準拠したオブジェクトのアクセサー メソッドの名前に対応します。 キーは ASCII エンコードを使用する必要があります。通常は小文字で始まり、空白を含めてはなりません。 したがって、上記の例では、NameクラスのプロパティPersonModelName Key Value になります。 公開するキーとプロパティの名前は同じである必要はありませんが、ほとんどの場合は同じです。

キー パスは、走査するオブジェクト プロパティの階層を指定するために使用されるドット区切りのキーの文字列です。 シーケンス内の最初のキーのプロパティは受信側に対して相対的であり、後続の各キーは前のプロパティの値を基準にして評価されます。 同様に、ドット表記を使用して、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 、要求を受け取る KVC クラスのインスタンスを基準にして、指定された Key の値を (a NSStringとして) 返します。 たとえば、上記で定義したクラスのインスタンスのPersonModel場合Personは、次のようになります。

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

プロパティの値を . に変更しますNameJane 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 の キー値監視プログラミング ガイドの概要を参照してください。

データ バインディング

次のセクションでは、C# コードを使用して値を読み書きするのではなく、キー値コーディングとキー値監視準拠クラスを使用して Xcode の Interface Builder の UI 要素にデータをバインドする方法について説明します。 この方法では、データ モデル表示に使用するビューから分離し、Xamarin.Mac アプリケーションの柔軟性とメイン容易にします。 また、記述する必要があるコードの量を大幅に減らします。

データ モデルの定義

インターフェイス ビルダーで 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
    }
}

このクラスのほとんどの機能については、上記の「キーと値の コーディング 」セクションで説明しました。 ただし、このクラスを配列コントローラーとツリー コントローラーのデータ モデルとして機能させるために行われたいくつかの特定の要素といくつかの追加を見てみましょう (後でデータ バインド ツリー ビュー、アウトライン ビューコレクション ビューに使用します)。

まず、従業員がマネージャーになる可能性があるため、(具体的にはNSMutableArray値を変更できるように) を使用NSArrayして、管理している従業員にアタッチできるようにします。

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

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

次の 2 つの点に注意してください。

  1. これは、テーブル ビュー、アウトライン ビュー、コレクションなどの AppKit コントロールへのデータ バインドの要件であるため、標準の C# 配列またはコレクションの代わりに使用NSMutableArrayしました
  2. データ バインディングの目的で配列を a 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 要素 (KVO 中) に Icon 反映されない場合があります。

[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キーのメッセージもDidChangeValueIcon送信WillChangeValueしているため、変更も表示されることに注意してください。

この記事の残りの部分では、 PersonModel データ モデルを使用します。

単純データ バインディング

データ モデルが定義された状態で、Xcode の Interface Builder でのデータ バインディングの簡単な例を見てみましょう。 たとえば、上記で定義した編集に使用できるフォームを Xamarin.Mac アプリケーションに PersonModel 追加しましょう。 モデルのプロパティを表示および編集するためのテキスト フィールドとチェック ボックスをいくつか追加します。

まず、インターフェイス ビルダーの 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

データキーを使用して公開したフォームに PersonModel フォームを Person バインドするには、次の操作を行います。

  1. 従業員名テキスト フィールドを選択し、Bindings Inspector切り替えます。

  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. [Employee is a Manager]\(従業員がマネージャーである\) チェックボックスを選択し、[バインド]ボックスをチェックし、ドロップダウンから[簡易ビュー コントローラ]を選択します。 次にキー パス入力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. これにより、プロパティfalseの値が ゚ の場合、ラベルが非表示になることがデータ バインディングにisManager通知されます。

  10. [従業員数] 管理テキスト フィールドに対して手順 7 と 8 を繰り返します。

  11. 変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。

アプリケーションを実行すると、プロパティの値が Person フォームに自動的に設定されます。

Showing an auto-populated form

ユーザーがフォームに加える変更は、ビュー コントローラーの Person プロパティに書き戻されます。 たとえば、従業員の選択を解除すると、マネージャーは Microsoft PersonModel のインスタンスをPerson更新し、[従業員数] 管理ラベルとテキスト フィールドは (データ バインディングを介して) 自動的に非表示になります。

Hiding the number of employees for non-managers

テーブル ビューのデータ バインディング

データ バインディングの基本を理解したので、配列コントローラーとテーブル ビューへのデータ バインディングを使用して、より複雑なデータ バインディング タスクを見てみましょう。 テーブル ビューの操作の詳細については、テーブル ビューのドキュメントを参照してください。

まず、インターフェイス ビルダーの 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データを読み書きできるように、特別に名前付きの 4 つのパブリック メソッドを公開しました。

次に、ビューが読み込まれるときに、配列に次のコードを設定する必要があります。

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し、[プラス] ボタンをクリックし、3 つのキーを追加します。 それらに名前を付け、Occupation次の操作を行isManagerいますName

    Adding the required key paths to the Object Controller.

  4. これにより、配列コントローラーが配列を管理しているものと、それが公開する必要があるプロパティ (キーを使用) が示されます。

  5. Bindings Inspector切り替え、[コンテンツ配列] で [バインド先] と [テーブル ビュー コントローラー] を選択します。 次の モデル キー パス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.

アウトライン ビューのデータ バインディング

アウトライン ビューに対するデータ バインディングは、テーブル ビューに対するバインドとよく似ています。 主な違いは、配列コントローラーの代わりにツリー コントローラー使用して、アウトライン ビューにバインドされたデータを提供することです。 アウトライン ビューの操作の詳細については、アウトライン ビューのドキュメントを参照してください。

まず、インターフェイス ビルダーの 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からのデータの読み取りと書き込みを行えるように、特別に名前が付けられた 4 つのパブリック メソッドを公開しました。

次に、ビューが読み込まれるときに、配列に次のコードを設定する必要があります。

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し、[プラス] ボタンをクリックし、3 つのキーを追加します。 それらに名前を付け、Occupation次の操作を行isManagerいますName

    Adding the required key paths for PersonModel.

  4. これにより、ツリー コントローラーに対して、配列の管理内容と、(キーを介して) 公開する必要があるプロパティが示されます。

  5. [ツリー コントローラ]セクションで、「子」入力personModelArrayし、[カウント]に「リーフ」と入力NumberOfEmployeesisEmployeeします。

    Setting the Tree Controller key paths

  6. これにより、ツリー コントローラーは、子ノードを検索する場所、子ノードの数、および現在のノードに子ノードがあるかどうかを示します。

  7. Bindings Inspector切り替え、[コンテンツ配列] で [バインド先] と [ファイルの所有者] を選択します。 次の モデル キー パス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) ファイル内でコレクション ビューを使用できません。 その結果、Xamarin.Mac アプリのコレクション ビューを定義するには、引き続き .xib ファイルを使用する必要があります。 詳細については、 コレクション ビュー のドキュメントを参照してください。

ネイティブ クラッシュのデバッグ

データ バインディングを間違えた場合 、アンマネージ コードで Native Crash が発生 し、Xamarin.Mac アプリケーションがエラーで SIGABRT 完全に失敗する可能性があります。

Example of a native crash dialog box

通常、データ バインディング中のネイティブ クラッシュの主な原因は 4 つあります。

  1. データ モデルの継承元 NSObject またはサブクラス NSObjectではありません。
  2. プロパティを属性の使用[Export("key-name")]にObjective-C公開しませんでした。
  3. アクセサーの値 WillChangeValue とメソッド呼び出しに対する変更をラップしませんでした (属性と DidChangeValue 同じキーを Export 指定します)。
  4. インターフェイス ビルダーのバインド インスペクターでキーの入力が間違っているか間違っています。

クラッシュのデコード

データ バインディングを見つけて修正する方法を示すことができるように、データ バインディングでネイティブ クラッシュを引き起こしましょう。 インターフェイス ビルダーで、コレクション ビューの例 Name の最初のラベルのバインドを次に Title変更します。

Editing the binding key

変更を保存し、Visual Studio for Mac に戻って Xcode と同期し、アプリケーションを実行しましょう。 コレクション ビューが表示されると、キーTitleを持つSIGABRTプロパティが公開されないため、アプリケーションは一時的にエラー (Visual Studio for Mac のアプリケーション出力に示されているように) PersonModel でクラッシュします。

Example of a binding error

アプリケーション出力のエラーの一番上までスクロールすると、問題を解決するための鍵が表示されます。

Finding the issue in the error log

この行は、バインドするオブジェクトにキー Title が存在しないことを示しています。 バインドを Interface Builder に戻し Name 、保存、同期、リビルド、実行すると、アプリケーションは問題なく正常に実行されます。

まとめ

この記事では、Xamarin.Mac アプリケーションでのデータ バインディングとキー値のコーディングの操作について詳しく説明しました。 最初に、キー値コーディング (KVC) とキー値監視 (KVO) を使用して C# クラス Objective-C を公開する方法について説明しました。 次に、KVO 準拠クラスを使用し、Xcode のインターフェイス ビルダーの UI 要素にデータ バインドする方法を示しました。 最後に、配列コントローラーとツリー コントローラーを使用した複雑なデータ バインディングを示しました。