エンジニア

Xamarin.Macアプリで、ドラッグ&ドロップした画像をウィンドウに表示する方法

投稿日:2020年7月2日 更新日:

こんにちは!今回ブログを担当することになりましたエンジニアの山元です。

デザイナー業務では、画像のリサイズ処理を必要とする状況がしばしば見受けられます。
そのため迅速に対応できる支援ツールがあれば負担軽減に繋がると考え、現在Xamarin.Macを利用して実装しています。

画像入力が簡単になるよう、ドラッグ&ドロップ機能を入れたいと調査していたのですが、
あまり情報を見つけられなかったこともあり、一連の実装作業をメモとして残そうと思います。
同じようなことをしたいと考えている方がいれば、参考にして頂ければ幸いです。

準備

実装環境は以下の通りです。
・macOS Catalina 10.15.4
・Visual Studio for Mac 8.5.4
・Xcode 11.5

初めに、Visual Studioで新しいプロジェクトを作成しておきます。
Cocoaアプリを選択し、任意のプロジェクト名と作成場所を設定する手順を踏みます。

↓ 作成されたプロジェクト

実装手順

以下の手順で進めます。

  1. Storyboard上のViewControllerScene内にCustomViewを配置し、操作用クラス”DropImageView”を設定
  2. 設定した”DropImageView”クラスに、ドラッグ&ドロップ用のコードを記述
  3. プロジェクトを実行し、任意の画像をウィンドウ内にドラッグ&ドロップして、画像が表示されたら完成

手順1

Visual Studioのソリューションエクスプローラーに"Main.storyboard"の項目があるので、右クリックして「プログラムから開く → Xcode Interface Builder」を選択し、Xcodeを開きます。

Xcodeの右上にある"+アイコン"(Library)を押し、CustomViewを検索してViewControllerScene内に配置します。

サイズや位置を変更して自由に配置した後、右側のユーティリティ・エリアに"Custom Class"の項目があるので、ここに任意のクラス名を記述します。

手順2

Visual Studioに戻ると自動的にXcodeと同期され、手順1で設定したクラスがソリューションエクスプローラーに追加されます。

ドラッグ&ドロップ用のコードを、追加されたクラスに記述していきます。
コードの全容は以下の通りです。

// This file has been autogenerated from a class added in the UI designer.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;

using Foundation;
using AppKit;
using CoreGraphics;

namespace Drag_and_Drop
{
    public partial class DropImageView : NSView
    {
        public DropImageView (IntPtr handle) : base (handle)
        {
            //初期化処理を呼ぶ
            this.Initialize();
        }

        //初期化
        private void Initialize()
        {
            //ファイルのドラッグ&ドロップを受付可能に
            this.RegisterForDraggedTypes(new string[] { NSPasteboard.NSFilenamesType });
        }

        //ドラッグされたファイルのドロップ判定
        public override NSDragOperation DraggingEntered(NSDraggingInfo sender)
        {
            //受け入れるファイルリスト
            var files = this.GetFiles(sender);

            //拡張子がpngで、1つのみドラッグしていた場合にドロップする
            if (files.Length == 1)
            {
                //ドラッグしたファイルをコピーしてドロップ
                return NSDragOperation.Copy;
            }

            return NSDragOperation.Generic;
        }

        //受け入れるファイルリストを取得
        private string[] GetFiles(NSDraggingInfo info)
        {
            //受け入れるファイルリスト
            var files = new List<string>();

            var availableType = info.DraggingPasteboard.GetAvailableTypeFromArray(RegisteredDragTypes());
            if (availableType == NSPasteboard.NSFilenamesType)
            {
                //ドラッグされてきたファイルリスト
                var lists = info.DraggingPasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType);
                if (lists is NSArray)
                {
                    //対象の拡張子だったら受け入れる (今回は"png")
                    var file = lists as NSArray;
                    for (nuint i = 0; i < file.Count; i++)
                    {
                        var item = file.GetItem<NSString>(i);
                        var ext = Path.GetExtension(item).ToLower();
                        if (ext == ".png")
                        {
                            files.Add(item);
                        }
                    }
                }
            }

            return files.ToArray();
        }

        //ドロップされたファイルの処理
        public override bool PerformDragOperation(NSDraggingInfo sender)
        {
            //受け入れるファイルリスト
            var files = this.GetFiles(sender);

            //ファイルを1つだけドラッグしていた場合のみ受け入れる
            if (files.Length != 1)
            {
                return false;
            }

            //ファイル情報を取得
            var file = files[0];
            var img = new NSImage(file);

            //ウィンドウ内に画像を表示するため、画像サイズを調整
            var imgResize = this.AspectFitSizeForMaxDimension(img);

            //既に画像が表示されていた場合は、その画像を削除する
            var drawImageViewChildren = this.Subviews;
            foreach (var child in drawImageViewChildren)
            {
                if (child is NSImageView)
                {
                    child.RemoveFromSuperview();
                    break;
                }
            }

            //画像をウィンドウ内に貼り付ける (中央に表示されるように位置を設定)
            var imgView = new NSImageView(new RectangleF(
                (float)this.Frame.Size.Width / 2 - (float)imgResize.Width / 2, (float)this.Frame.Size.Height / 2 - (float)imgResize.Height / 2, (float)imgResize.Width, (float)imgResize.Height
                ))
            {
                Image = img
            };
            this.AddSubview(imgView);

            return true;
        }

        private CGSize AspectFitSizeForMaxDimension(NSImage img)
        {
            //ウィンドウサイズ
            var minSize = Math.Min(this.Frame.Size.Width, this.Frame.Size.Height);
            float maxDimension = (float)minSize - 10.0f;

            //画像サイズ
            var imgWidth = (float)img.Size.Width;
            var imgHeight = (float)img.Size.Height;

            //ウィンドウサイズより画像が大きければ、比率を保ったまま画像を縮小させる
            if (imgWidth > maxDimension || imgHeight > maxDimension)
            {
                if (imgWidth == imgHeight)
                {
                    imgWidth = maxDimension;
                    imgHeight = maxDimension;
                }
                else if (imgWidth > imgHeight)
                {
                    var aspectRatio = imgWidth / imgHeight;
                    imgWidth = maxDimension;
                    imgHeight = maxDimension / aspectRatio;
                }
                else if (imgWidth < imgHeight)
                {
                    var aspectRatio = imgHeight / imgWidth;
                    imgWidth = maxDimension / aspectRatio;
                    imgHeight = maxDimension;
                }
            }

            CGSize resize = new CGSize(imgWidth, imgHeight);
            return resize;
        }
    }
}

手順3

プロジェクトを実行し、任意のpngファイルをドラッグしてウィンドウ上にドロップすると、画像が表示されます。
続けて他pngファイルをドラッグ&ドロップすると、新しい画像に更新されます。

まとめ

ドラッグ&ドロップ処理について、基本的な実装を紹介しました。
他にも複数画像をドラッグ&ドロップしてウィンドウ内にランダムに配置したり、ウィンドウ内の画像をドラッグして自由に移動させたりなど、色々応用が効きます。
今回の内容から発展させて試してみてくださいね。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-,

© WonderPlanet Inc.