![[Xamarin.Mac] データバインディングでコントロールと連携してみました](https://devio2023-media.developers.io/wp-content/uploads/2019/07/eyecatch_xamarin_2.png)
[Xamarin.Mac] データバインディングでコントロールと連携してみました
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Xamarin.Macを使用すると、C#でネイティブなMacのアプリが作成可能です。 ここでは、私自身がXamarin.Macに入門して学習した事項を覚書として書かせて頂いています。
今回は、データバインディングを使用して、コントロールと連携してみました。
Xamarin.Macでは、規定の属性を設定することで、C#からKey-Value Observing Programmingにアクセスが可能です。
2 商品クラス
簡単なクラスを元に確認を進めます。
下記は、「名前」と「価格」だけを保持する「商品」クラスです。名前(_name)と価格(_price)をプライベートで保持し、アクセス要領をプロパティ(Name及び、Age)で公開しています。
public class Product {
    private string _name;
    private int _price;
    public Product(string name, int price) {
        _name = name;
        _price = price;
    }
    public string Name {
        get { return _name; }
        set { _name = value; }
    }
    public int Price {
        get { return _price; }
        set { _price = value; }
    }
}
3 Objective-Cにクラスを公開
続いて、商品クラスをデータバインディング可能なクラスに変更します。 作業は、以下の3つです。
- NSObject(または、NSObjectを継承クラス)から継承
 - Registerでクラスを登録
 - Exportでプロパティを公開
 - WillChangeValue及び、DidChangeValueで通知先プロパティを指定
 
なお、RegisterとExportで指定する名前は、あくまでObjective-Cに向けて公開される名前であって、元のクラス名やプロパティ名と一致している必要はありません。
[Register("Product")]
public class Product : NSObject {
    private string _name;
    private int _price;
    public Product(string name, int price) {
        _name = name;
        _price = price;
    }
    [Export("Name")]
    public string Name {
        get { return _name; }
        set {
            WillChangeValue("Name");
            _name = value;
            DidChangeValue("Name");
        }
    }
    [Export("Price")]
    public int Price {
        get { return _price; }
        set {
            WillChangeValue("Price");
            _price = value;
            DidChangeValue("Price");
        }
    }
}
4 操作
(1) ValueForKey / SetValueForKey
データバインディング可能になったクラスは、ValueForKey及び、SetValueForKeyでプロパティ値の取得・設定が可能になります。
// 商品クラスを生成
var product = new Product("AAA",200);
// 名前を変更
product.SetValueForKey(new NSString("BBB"), new NSString("Name"));
// 名前及び、価格を取得
var name = product.ValueForKey(new NSString("Name"));
var price = product.ValueForKey(new NSString("Price"));
// 確認
Console.WriteLine($"name={name} price={price}");
出力
name=BBB price=200
(2) AddObserver
AddObserverで、指定したプロパティの変化を受け取ることが出来ます。
// 商品クラスを生成
var product = new Product("AAA",200);
// オブザーバを設定(Nameプロパティの値が変化した時にイベントを登録)
product.AddObserver("Name", NSKeyValueObservingOptions.New, (sender) => {
    // 変化した値を確認
    Console.WriteLine($"New Name: {product.Name}");
});
// 名前の変更
product.SetValueForKey(new NSString("BBB"), new NSString("Name"));
// 名前及び、価格を取得
var name = product.ValueForKey(new NSString("Name"));
var price = product.ValueForKey(new NSString("Price"));
// 確認
Console.WriteLine($"name={name} price={price}");
出力
New Name: BBB New Name: BBB name=BBB price=200
5 コントロールとの同期
簡単なUIを作成して、商品クラスとデータ連携してみました。
(1) データの公開
ViewControllerで保持されるデータ(product)を、プロパティとして定義し、[Export("Product")] で公開します。
public partial class ViewController : NSViewController {
    // データの定義(生成)
    Product product = new Product("AAA", 100);
    // プロパティとして公開
    [Export("Product")]
    public Product Product {
        get { return product; }
        set {
            WillChangeValue("Product");
            product = value;
            DidChangeValue("Product");
        }
    }
    public override void ViewDidLoad() {
        base.ViewDidLoad();
        // 変化を確認するために、オブザーバーを定義する
        product.AddObserver("Name", NSKeyValueObservingOptions.New, (sender) => {
            Console.WriteLine($"New Name: {product.Name}");
        });
    }
(2) Interface Builder から bind
ウインドウにText Fieldを追加し、バインド設定します。
Bind toにチェックを入れて、バインド先をViewControllerとし、その中の、ProductのNameに紐づけています。

self.Product.Name
ここで、selfは、当該コントロールが配置されているビューコントローラーで、ここでは、ViewControllerとなります。
実行してみると、初期化されたProduct.Nameの値である「AAA」が、TextFieltの値として表示され、変更すると、Observerでトリガしたイベントにより、その内容がコンソールに出力されている事が確認できます。

6 最後に
今回は、簡単なデータバインディングの動作を確認してみました。
UIコントロールへの表示や、UIで変更された値の反映は、様々な実装が必要になりますが、バインディングを使用することで、その実装量は結構省力化されるかも知れません。 ビューとロジックとの分離のためにも、積極的に利用していきたいところです。







