この記事は公開されてから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で変更された値の反映は、様々な実装が必要になりますが、バインディングを使用することで、その実装量は結構省力化されるかも知れません。 ビューとロジックとの分離のためにも、積極的に利用していきたいところです。