ぷろじぇくと、みすじら。

LINQPadのコードにファイルをドラッグアンドドロップで渡したい

Created at:

LINQPadでちょっとしたツールやバッチスクリプト的なものを作ったときに処理対象のとしてファイルを取ることはよくあるのですが、大抵はコードにパスを直接書いていることがほとんどではないでしょうか。

ただ便利なものは往々にして対象ファイルを変えたりしたいとか誰かに渡したいこともあり、そんな時処理対象のファイルをドラッグアンドドロップでコードに渡せれば…と思ったりすることもあります。残念ながらLINQPadに標準で備わっているユーザー入力と言えば Console.ReadLineHyperlinq ぐらいでドロップを受け付けるユーティリティは備わっていません。

その時点で普通のWindowsアプリケーションとして作りましょうという気もするのですがそうはいってもめんどくさいですよね。というわけでなんとか渡せるようにする方法です。

解決策

LINQPadは通常出力をHTMLベースで行っていますが、自力でHTML以外のカスタム出力結果パネルを出すこともできます。カスタムな出力結果パネルはWindows FormsやWPFのコントロールをホストできるというものなので、それを利用して通常のWindowsのアプリケーションと同様にドラッグアンドドロップを受け付けるという形でドロップターゲット機能を実現できます。

というわけで以下のようなメソッドを用意することでドロップ用のパネルを表示し、ドロップしたファイルのパスを返せます。

// PresentationFramework.dll と PresentationCore.dll を References に追加しておきます
Task<string[]> WaitForFileDrop()
{
    var tcs = new TaskCompletionSource<string[]>();
    var running = Util.KeepRunning();
    var button = new System.Windows.Controls.Button
    {
        Content = "Drop Here",
        AllowDrop = true,
    };
    var outputPanel = PanelManager.DisplayWpfElement(button, "File Drop");
    button.DragEnter += (sender, e) =>
    {
        e.Effects = System.Windows.DragDropEffects.Copy;
    };
    button.Drop += (sender, e) =>
    {
        var dropPaths = e.Data.GetData(System.Windows.DataFormats.FileDrop) as string[];
        
        running.Dispose();
        outputPanel.Close();
        
        tcs.SetResult(dropPaths);
    };

    return tcs.Task;
}

やっていることは単純でドロップを受け付けるボタンを作り、出力パネルとして表示し、ドロップされたらパスを取り出して TaskCompilationSource<T> を通して呼びもとに返すだけです。

使い方はこんな感じです。必要に応じてMyExtensionsなどに収めておくと使い勝手がいいでしょう。

async Task Main()
{
    // ドロップを待ち受け
    var paths = await WaitForFileDrop();

    // ドロップされたファイルのパスを出力
    paths.Dump();

    // ドロップされたファイルを画像として表示
    paths.Select(x => Util.Image(x)).Dump();
}

これで人に渡したりバッチ的に使うときにちょっと便利に使えるようになるかもしれません。どうぞご利用ください。