Xamarin
Xamarin.Forms
Xamarin.Mac

Xamarin.MacのウインドウでDrag&Dropを受け入れる

Xamarin.MacにDrag&Drop

忘備録です。

Window(正確にはView)でDrag&Dropを受け入れる方法です。
と言っても、Objective-Cと同じことするだけなのですが。

なお、アドベントカレンダーで書いた方法でXamarin.FormsのページをViewの上に被せる方法を使った場合でも親ViewでDrag&Dropを捕まえてくれます。

ファイルをDropできるようにする

NSViewのサブクラスを作成する

namespace Views
{
    public class MainView : AppKit.NSView
    {
        public MainView(IntPtr handle) : base(handle)
        {
            Initialize();
        }

        [Export("initWithCoder:")]
        public MainView(NSCoder coder) : base(coder)
        {
            Initialize();
        }

        void Initialize()
        {
        }
    }
}

ソリューションビュー内で 右クリック→追加→新しいファイル として、サブクラスを作り、xibでもstoryboardでも割り付けて使えるようにします。

Dropできる形式を登録する

Initialize関数内でDropできる形式を登録します。

        void Initialize()
        {
            RegisterForDraggedTypes(new string[] { NSPasteboard.NSFilenamesType });
        }

これで、View内でファイルをDropできると認識させておきます。

DragされてきたときにDropで受け入れるかチェックする

        string[] GetFilesFromDraggingInfo(NSDraggingInfo info)
        {
            var pb = info.DraggingPasteboard;

            var files = new List<string>();
            var t = pb.GetAvailableTypeFromArray(RegisteredDragTypes());
            if (t == NSPasteboard.NSFilenamesType)
            {
                var lists = pb.GetPropertyListForType(NSPasteboard.NSFilenamesType);
                if (lists is NSArray)
                {
                    var arr = lists as NSArray;
                    for (nuint i = 0; i < arr.Count; i++)
                    {
                        var item = arr.GetItem<NSString>(i);
                        var ext = Path.GetExtension(item).ToLower();
                        if (ext == ".epub")
                        {
                            files.Add(item);
                        }
                    }
                }
            }

            return files.ToArray();
        }

        public override NSDragOperation DraggingEntered(NSDraggingInfo sender)
        {
            var files = GetFilesFromDraggingInfo(sender);

            if(files.Length > 0)
            {
                return NSDragOperation.Copy;
            }

            return NSDragOperation.Generic;
        }

GetFilesFromDraggingInfo関数で行なっているのは、Dragされてきたものがファイルで拡張子がepubか確認し、そのファイル名のみを配列で抜き出す、ということを行なっています。

DraggingEntered関数は、DragされてView内に入ってきたときに呼び出される関数で、受け入れるなら受け入れる方法を返答します。
以下の形式を返すようにします。

NSDragOperation.None
何もしない
NSDragOperation.Copy
コピーとして受け入れる
NSDragOperation.Link
リンクとして受け入れる
NSDragOperation.Move
移動として受け入れる
NSDragOperation.Generic
何もしない(Noneとは意味合いが違う)

返答により、マウスカーソル周りの表示が色々変わります。

Dropを受け入れる

        public override bool PerformDragOperation(NSDraggingInfo sender)
        {
            var files = GetFilesFromDraggingInfo(sender);

            foreach (var file in files)
            {
                Debug.WriteLine($"drop file:{file}");
            }

            return files.Length > 0;
        }

PerformDragOperationはDropを受け入れるかどうかを最終的に判断する部分です。
マウスボタンを離した瞬間に呼ばれます。
trueを返すと受け入れ、falseを返すと受け入れない、となっており、これによって戻っていくアニメーションなどが表示されます。

Object-Cそのまま

実は、ここ(ドラッグ&ドロップされた複数のファイルのパスを得るカスタムView:Qiita)とほとんど同じです。
見比べればXamarin.Macの作法的なものがわかります?