[Xamarin.Mac] フォルダ内の画像を一覧表示してみました

2020.12.05

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

1 はじめに

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

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

今回は、フォルダを指定して、その中にある画像ファイルを一覧表示してみました。

2 画面構成

Interface Builderでの画面設計です。

最初に、ウインドウいっぱいに上下分割のSplit Viewを置きました。そして、上側のビューの高さを固定し、ImageButtonを配置しました。

Outletは、ImageButton(NSButton)とCollectionView(NSCollectionView)という名前で作成しています。

3 NSCollectionViewItem

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

この時、以下の3つのファイルが生成されますが、PictureView.xibは、PictureViewController.xibにRenameしています。

  • PictureView.cs
  • PictureView.xib
  • PictureViewController.cs

Interface Builderで、LabelとImageViewを配置しました。

Image Viewは、Owner(PictureViewController.cs)のImageプロパティにバインディングしています。

Labelは、OwnerのNameプロパティにバインディングしています。

PictureViewController.csは、継承元をNSCollectionViewItemに変更し、バインディング用に2つのプロパティを公開しています。

PictureViewController.cs

public partial class PictureViewController : NSCollectionViewItem {

    NSImage _image;
    string _name;

    [Export("Image")]
    public NSImage Image {
        get {
            return _image;
        }
        set {
            WillChangeValue("Image");
            _image = value;
            DidChangeValue("Image");
        }
    }

    [Export("Name")]
    public string Name {
        get { return _name; }
        set {
            WillChangeValue("Name");
            _name = value;
            DidChangeValue("Name");
        }
    }
// ・・・略・・・
}

4 Delegate/DataSource

デリゲートは、NSCollectionViewDelegateFlowLayoutを継承して作成しています。

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

データソースは、NSCollectionViewDataSourceを継承して作成し、データとして、ファイルパスの一覧を保持させます。

データには、画像ファイルのパスが入っているので、GetItem()の段階で、NSImageを生成して、PictureViewControllerのプロパティを更新しています。

public class CollectionViewDataSource : NSCollectionViewDataSource {

    // ファイル(パス)の一覧
    public List<string> Files = new List<String>();

    public CollectionViewDataSource() {}

    public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath) {
        var item = collectionView.MakeItem("cell", indexPath) as PictureViewController;
        var file = Files[(int)indexPath.Item];
        // ファイル名
        item.Name = Path.GetFileName(file);
        // 画像
        item.Image = new NSImage(file);
        return item;
    }

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

5 ViewController

ViewControllerViewDidLoad() で、作成したデータソースとデリゲートをCollection Viewにセットしています。

また、ボタンをクリックされた際には、ディレクトリ選択のダイアログを表示し、選択された場合は、その中のファイル(jpg)一覧で、データソースを初期化します。

ViewController.cs

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

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

        // Openボタンがクリックれた時のイベントを定義
        ImageButton.Activated += ImageButton_Activated;

        // PictureViewControllerの登録
        CollectionView.RegisterClassForItem(typeof(PictureViewController), "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();

    }

    // Openクリック時の処理
    private void ImageButton_Activated(object sender, EventArgs e) {
        // ディレクトリ選択
        var dlg = NSOpenPanel.OpenPanel;
        dlg.CanChooseDirectories = true;
        if(dlg.RunModal() == 1) {
            var url = dlg.Urls[0];
            if(url != null) {
                // 選択されたディレクトリ内のjpgの一覧を取得
                string[] files = Directory.GetFiles(url.Path, "*.jpg");
                // Collection Viewのデータソースを更新する
                ((CollectionViewDataSource)CollectionView.DataSource).Files = files.ToList();
            }
        }
        CollectionView.ReloadData();
    }
    // ・・・略・・・
}

6 最後に

今回は、フォルダを指定して画像ファイルを一覧してみました。データバインディングのコツが掴めれば、比較的にスムーズに実装が進みそうな気がしてます。

個人的には、ImageViewのバインディングタブでは、Dataが一番上になっているので、間違ってこちらでBind toしないように注意が必要かなと思いました。