2年以上前のことですが、ランチャーアプリをつくりました。

ランチャーアプリの作り方

その記事にコメントが来ました。

ドラッグアンドドロップでアイコンの入れ替えができるようになると最高なのですが、可能なのでしょうか?

もちろん可能です。このアプリはC# Microsoft .NET Frameworkで作られています。ただ.NET Frameworkバージョン4.8をもってメジャーアップデートを終了することがアナウンスされているので、今回は.NET Windowsフォームアプリで作成します。.NET Frameworkのときは問題なく動いていたコードではダメな部分もあったので作り直すことにします。

基本的な作り方は同じです。PanelのうえにPictureBoxを貼り付けたものをフォーム上にならべます。ファイル(ショートカットでもOK)をドラッグアンドドロップするとPictureBoxのうえにアイコンが表示され、これをクリックするとファイルが開くというものです。

PanelExクラスの定義

Panelクラスを継承してPanelExクラスをつくります。

まずコンストラクタと主なフィールド変数、定数を示します。

大きさは縦横32ピクセルです。ドラッグアンドドロップで並べ替えをするときにどれが移動対象になるのかがわかるようにIndexというフィールド変数を定義しています。またクリックするとファイルやフォルダを開かないといけないのでファイルのパスを格納する_pathというフィールド変数も定義しています。

コンストラクタが呼び出されるとPictureBoxが生成され、Panelのうえを完全に覆います。そのためクリックやドラッグやドロップのイベントはPictureBoxで発生します。これに対応できるようにイベントハンドラを追加しています。イベントハンドラの名前の最後に2がついているものはドラッグアンドドロップで順番を変更するときに必要になるものです。

ファイルやフォルダがドラッグアンドドロップされたとき

ファイルやフォルダがドラッグアンドドロップされたときの処理を示します。ドラッグされているものやドロップされたものがDataFormats.FileDropでない場合は無視です。

ファイルまたはフォルダ(ショートカットも含む)が1つだけドラッグされている場合はe.Effect = DragDropEffects.Copy;とすることでマウスポインタの形状をコピーをするときの形に変更します。

ドロップ時の処理を示します。ドロップされたものがファイルまたはフォルダ(ショートカットも含む)であり、その数が1つだけだったときはSetPathメソッドを呼び出して登録の処理をおこないます。

SetPathメソッドを示します。

登録するときにアイコンもいっしょに登録したいものです。ファイルの場合は比較的簡単です。Icon.ExtractAssociatedIconメソッドを使えば、指定したファイルに含まれているイメージのアイコン表現を取得することができるのです。アイコンが取得できたらこれをBitmapに変換してPictureBoxに表示させます。

フォルダの場合はAPI関数を使います。この処理は後述するGetFolderImageメソッドでおこないます。

フォルダのアイコンを取得する処理を示します。SHGetFileInfo関数を使うとフォルダのアイコンを取得することができます。失敗したときはnullが返されるのでPictureBoxにはなにも表示させません。

GetPathメソッドは外部からファイルのパスを取得するときに使います。

ランチャーとして機能させる

PictureBoxをクリックしたら登録されているファイルやフォルダを開きます。.NET FrameworkでプログラミングしていたときはSystem.Diagnostics.Process.Start(path)でよかったのですが、.NET Windowsフォームアプリの場合はSystem.Diagnostics.Process.Start(“Explorer.exe”, path)としないとアクセス拒否の例外が発生してしまいます。

もしクリックしたところになにも登録されていない場合はメッセージボックスを表示します。

PictureBoxの上をマウスが移動したときにファイルやフォルダが登録されている場合はそのパスを表示します。またForm1クラスの説明はしていないのでForm1.ShowFilePathメソッドって何だ?と思うかもしれませんが、フォーム上にはファイルパスを表示するためのLabelを設置しています。Form1.ShowFilePathメソッドはそのLabelにパスを表示させます。

ドラッグアンドドロップで入れ替える

ここからが本番です。ドラッグアンドドロップで登録したパスの順序を入れ替えます。

ドラッグアンドドロップで順序を入れ替えるためには入れ替えたいパスが登録されているPictureBoxをクリックしなければなりません。このときファイルを開く処理と区別するためにマウスボタンがおされてもすぐにドラッグの開始処理をおこないません。マウスボタンをおしたまま一定の距離を移動したときにドラッグ開始と判断します。

マウスボタンがおされたらその座標を記憶します。そしてドラッグが開始されることなくマウスボタンが離された場合はPoint.Emptyを代入します。

PictureBoxの上でマウスボタンがおされたままX方向またはY方向に10ピクセル以上移動したらドラッグの開始と判断します。

ドラッグされているときにe.Data.GetDataPresent(typeof(PanelEx))であれば移動のためのドラッグがおこなわれていることになります。この場合はマウスポインタの形状を「移動」の形に変更します。

ドロップされたときの処理を示します。ドロップされた先になにも登録されていない場合はその位置に移動して移動元の情報は削除します。ドロップされた先に他のデータが登録されている場合はそれ以降のデータをずらします。

データをずらすときの処理ですが、フォーム上に存在するPanelExのパスをすべて取得します。そして移動元のデータを抜き出して移動先のデータがあるところに割り込ませます。そしてそのデータをフォーム上に存在するすべてのPanelExに再度割り当てます。

Form1クラスにおける処理

Form1クラスにおける処理ですが、コンストラクタ内で必要な数のPanelExを生成してフォーム上に配置します。ここでは4行6列に配置しています。そしてその下にパスを表示するためのLabelを配置しています。パスが長いと複数行になってしまうことが予想されます。そこでAutoSizeはfalseを設定して文字列が端まで来たら折り返して表示するようにしています。

PanelExクラス内で呼び出していたGetPanelListメソッドとShowFilePathメソッドもここで定義しています。

登録データの保存と読み出し

それからデータを保存する処理と読み出す処理、右上の×ボタンを押しても終了しない(タスクトレーのなかに入る)ようにするためのコードも示します。

常駐アプリとして使う

×ボタンをクリックしたらタスクトレーのなかに入るようにするには以下のようにします。

OnFormClosingをオーバーライドすることで終了しないアプリをつくることができます。ここではタスクトレーのなかにアイコンをつねに表示するようにして×ボタンが押されたらフォームを非表示にしています。IsEndフラグがfalseの場合、フォームが非表示になるだけで終了することはありません。

これでは本当に終了させたいときに終了できなくなるので終了するために別の手段を用意します。メニューで終了を選択するとIsEndフラグがtrueになってApplication.Exit()が呼び出されます。今度はIsEndフラグがtrueなのでアプリケーションを終了させることができます。

それからフォームが非表示になっている場合はメニューで終了を選択することができないので、フォームを再表示させるための手段も用意します。タスクトレイに表示されている通知アイコンがクリックされたときは非表示になっているフォームを表示させます。