[Xamarin.Mac] コレクションビューを使用してみました

2020.12.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

Xamarin.Macを使用すると、C#でネイティブなMacのアプリが作成可能です。 ここでは、私自身がXamarin.Macに入門して学習した事項を覚書として書かせて頂いています。

今回は、コレクションビューを使用してみました。

コレクションビューは、テーブルビューやアウトラインビューと違って、個々のアイテムを表示するための組み込みビューがありません。 これを独自に用意する必要がある事から、やや、ハードルの高いものとなっているかも知れません。

2 CollectionViewの配置

Interface Builderで、メインとなるビューにCollection Viewを配置し、ウインドウいっぱいに表示されるように制約を追加します。

また、AssistantエディタでViewController.hを開いてOutletを接続します。 ( 接続するコントロールがNSCollectionViewになっていることに注意が必要です。)

3 ビューの作成

ファイル > 新しいファイル > Mac > View Controllerと辿り、新規にビューを作成します。(ここでは、名前をMyItemとしました)

この時、以下の3つのファイルが生成されます。

  • MyItem.cs
  • MyItem.xib
  • MyItemController.cs

操作の対象を、MyItemController.csとしたいのですが、これをプロトタイプとしてビューコントローラーを登録するとき、xibと名前が一致している必要があるため、MyItem.xibをMyItemController.xibに変更します。

続いて、CollectionViewItem.xibをInterface Builderで開いて、Boxを配置し、ビューいっぱいに表示されるように制約を追加します。Boxは、デフォルトでタイトル(Box)が表示される状態になっているので、とりあえずそのままにしておきます。

MyItemControllerクラスは、NSCollectionViewItemを継承するように書き換えます。

MyItemController.cs

public partial class MyItemController : NSCollectionViewItem {
        #region Constructors

        // Called when created from unmanaged code
        public MyItemController(IntPtr handle) : base(handle) {
            Initialize();
        }

4 DataSource/Delegate

コレクションビューのデリゲートクラスとしてNSCollectionViewDelegateFlowLayoutを継承した、CollectionViewDelegateクラスを作成します。

NSCollectionViewDelegateFlowLayoutは、組み込みのレイアウト型です。

using AppKit;

namespace Sample001 {
    public class CollectionViewDelegate : NSCollectionViewDelegateFlowLayout {
        public CollectionViewDelegate() {
        }
    }
}

データクラスは、NSCollectionViewDataSourceを継承して作成します。

GetNumberofItems()は、データ数を返すメソッドですが、ここでは、仮に10個アイテムがあるという想定で、10を返しておきます。

GetItem()は、ここのアイテム(NSCollectionViewItem)を返すメソッドなので、MyItemControllerクラスを生成して返しています。

namespace Sample001 {
    public class CollectionViewDataSource: NSCollectionViewDataSource {
        public CollectionViewDataSource() {
        }

        public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath) {
            var item = collectionView.MakeItem("cell", indexPath) as MyItemController;
            return item;
        }

        public override nint GetNumberofItems(NSCollectionView collectionView, nint section) {
            return 10;
        }
    }
}

5 初期化

最後に、ViewControllerのViewDidLoad()で、コレクションビューの初期化を行います。

NSCollectionViewFlowLayout() で、150✕150のレイアウトを定義して、コレクションビューに設定してます。(①)

個々のアイテムを表示するために準備した、MyItemControllerは、RegisterClassForItemでコレクションビューに登録しています。(②)

Delegate及び、DataSourceも、通常通り登録しています。(③、④)

public partial class ViewController : NSViewController {
        public ViewController(IntPtr handle) : base(handle) {
        }

        public override void ViewDidLoad() {
            base.ViewDidLoad();

            // MyItemControllerの登録(②)
            CollectionView.RegisterClassForItem(typeof(MyItemController), "cell");

            // DataSource(③)
            CollectionView.DataSource = new CollectionViewDataSource();

            // Flow layout(①)
            var flowLayout = new NSCollectionViewFlowLayout() {
                ItemSize = new CGSize(150, 150),
                SectionInset = new NSEdgeInsets(10, 10, 10, 20),
                MinimumInteritemSpacing = 10,
                MinimumLineSpacing = 10
            };
            CollectionView.WantsLayer = true;
            CollectionView.CollectionViewLayout = flowLayout;

            // Delegate(④)
            CollectionView.Delegate = new CollectionViewDelegate();

        }

ここまでの実装で、10個のアイテムが表示されていることを確認できます。

6 データ表示

元となるデータクラスは、下記で作成したものを使用します。

Product.cs

[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("Name");
            _price = value;
            DidChangeValue("Name");
        }
    }
}

MyItemControllerで、データクラスをプロパティとして定義します。

MyItemController.cs

public partial class MyItemController : NSCollectionViewItem {

    // 商品(Product)クラスを定義
    private Product _product;

    // 商品(Product)クラスを公開
    [Export("Product")]
    public Product Product {
        get { return _product; }
        set {
            WillChangeValue("Product");
            _product = value;
            DidChangeValue("Product");
        }
    }

ビューにLabelを追加して、公開されたプロパティにバインディングします。

データソースで商品クラスの配列(Data)を定義し、GetItem() 及び、GetNumberofItems() で、それを返すように書き換えます。

CollectionViewDataSource.cs

public class CollectionViewDataSource: NSCollectionViewDataSource {

        public List<Product> Data;

        public CollectionViewDataSource() {

        }

        public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath) {
            var item = collectionView.MakeItem("cell", indexPath) as MyItemController;
            item.Product = Data[(int)indexPath.Item];
            return item;
        }

        public override nint GetNumberofItems(NSCollectionView collectionView, nint section) {
            return Data.Count;
        }
    }

データソースのデータは、ViewControllerViewDidLoadで生成します。

ViewController.cs

<br />public partial class ViewController : NSViewController {

    public override void ViewDidLoad() {
        base.ViewDidLoad();

        // ・・・略・・・

        // データ
        var Products = new List<Product>();
        Products.Add(new Product("AAA", 100));
        Products.Add(new Product("BBB", 120));
        Products.Add(new Product("CCC", 1300));
        Products.Add(new Product("DDD", 220));
        Products.Add(new Product("EEE", 950));
        ((CollectionViewDataSource)CollectionView.DataSource).Data = Products;
    }

実行すると、定義した商品データの表示を確認できます。

7 最後に

今回は、コレクションビューを使用してみました。

アイテム用のビューの準備や、レイアウト定義など、色々作業が多くなっています。また、データバインディングも必須となることから、やはり、コレクションビューの利用は、ちょっと大変です。