Visual Basic 初級講座 |
Visual Basic 中学校 > 初級講座 >
第22回 ドラッグ&ドロップ
マウスの操作で文字や画像・ファイルといったデータを操作する「ドラッグ&ドロップ」は快適な操作にはかかせない動作です。今回はこのドラッグ&ドロップの基本について解説します。
この回の要約 ・コントロールにドロップできるようにするには
・ドラッグするにはDoDragDropメソッドを使う ・ファイルのドラッグ&ドロップではファイルはあらかじめ存在していなければならず、ドラッグデータとしては必ず「ファイル名の配列」を使用する。 ・コントロールをマウスでドラッグして移動する方法 |
あなたが作ったアプリケーションを他人に使ってもらおうとするなら使いやすいようにプログラムしなければなりません。どうすれば使いやすいか考えるとなかなか難しいものがあるのですが、誰でも賛成できる点として「他のアプリケーションと同じ感覚で操作できること」が挙げられるでしょう。
たとえば、Wordの操作に慣れている人ならメモ帳は初めてでもすらすら使えます。もし、その人が音楽作成ソフトCubaseをはじめて使ったとすると使い方がほとんどわからないでしょうが、ファイルを開いたり保存したりするためには[ファイル]メニューを使えばよいと言うことはすぐにわかります。これは驚くべきことではありませんか?まったく初めてのソフト、初めてのジャンルなのに操作方法がすぐにわかるのですよ!
このように、「他のアプリケーションと同じ感覚」ということはとても重要です。あなたのアプリケーションは独自の操作方法を採用していませんか?それがよほど画期的で優れていると言う自信がない限り、独自の操作方法はあきらめて他のアプリケーションと同じ操作方法を採用しましょう。たとえば、ファイルを開くときには[ファイル]メニューを使うか、ツールバーのアイコンを使うようにしましょう。
さて、多くの市販のアプリケーションが実装しているのに、多くのVBプログラマが無視している操作方法があります。それが今回のテーマ「ドラッグ&ドロップ」です。
みなさんのソフトにもドラッグ&ドロップを実装して親切で使いやすいソフトを目指しましょう。
参考 VB6との違い ドラッグ&ドロップの構造的な仕組みについてはVB6からVB2005まで同じですが、その手法となるとVB.NET2002の時点で大きく変更されました。 特にVB6の場合は同じアプリケーション内のドラッグ&ドロップと、他のアプリケーションとの間で行うOLEドラッグ&ドロップが明確に区別されていましたが、VB.NET2002以降ではこれらは同じ手法で扱うようになりました。 |
では、簡単な「ドロップ」の方から説明しましょう。ドロップはドラッグされてきたものを受け入れる動作です。
今回はフォームにテキストボックスを1つ配置して、外部のアプリケーションからこのテキストボックスへのドラッグ&ドロップを実現させます。「外部のアプリケーション」はドラッグ&ドロップ対応のアプリケーションなら何でも良いのですが、ここではWindowsに付属しているワードパッドを例にとって説明します。
プログラムを紹介する前にドロップの流れについて簡単に説明します。
何かがドラッグされてくると、コントロールのDragEnterイベント(読み方:DragEnter = ドラッグエンター)が発生します。DragEnterイベントでは、このコントロールがどのようなドロップを受け入れるのかをドラッグ元に通知しなければなりません。ここで通知をしないとドラッグ元は「ここにはドロップできない」と判断してしまいます。また、ここでの通知はドロップされた際のドラッグ元の反応にも影響を与えます。
いよいよマウスボタンが離されると、今度はDragDropイベント(読み方:DragDrop = ドラッグドロップ)が発生します。ここではドラッグされたデータを受け取って具体的にどのようなことをするのかプログラムします。プログラムは何でも書けますので予想外の動作を行わせることもできますが、通常はドラッグされてきたデータを表示したりします。
なお、ドロップ対象となるコントロールは、AllowDorpプロパティ(読み方:AllowDrop = アロゥドロップ)をTrueにしておかなければなりません。いくらプログラムしてもAllowDropプロパティがFalseではドラッグドロップを受け入れることができないので注意してください。
以上の一連の流れを具体的にプログラムすると次のようになります。AllowDropプロパティはプロパティウィンドウを使ってあらかじめTrueにしておいてください。
Private Sub
TextBox1_DragEnter(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles TextBox1.DragEnter
'ドラッグされている内容が文字列型に変換可能な場合 |
Private
Sub
TextBox1_DragDrop(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles TextBox1.DragDrop
'ドロップされた内容を表示する End Sub |
■リスト1:単純なテキストのドロップ
これで、ワードパッドからこのテキストボックスに文字列をドラッグしてコピーすることができるようになりました。試すにはワードパッドに何か入力してから何文字か選択して、その選択部分をテキストボックスまでマウスでドラッグしてください。
ドラッグ&ドロップの具体的なデータや効果・マウス・キーボードの状態の管理にはDragEventArgs(読み方:DragEventArgs = ドラッグイベントオーグス)を使います。
DragEventArgsはドラッグ&ドロップ関連のイベントの第2引数となっていて、次の処理を行うことができます。
処理 | 説明 |
データ | Dataプロパティを使ってドラッグ&ドロップに関連付けられたデータの操作・情報の取得を行うことができます。 |
キーボード・マウスのボタン | KeyStateプロパティを使うと、キーボードの[Ctrl]キー、[Alt]キー、[Shift]キーの状態や、マウスのボタンの状態を取得することができます。 |
マウスの位置 | Xプロパティ、Yプロパティでマウスの座標がわかります。 |
ドラッグ&ドロップ効果 | EffectプロパティとAllowedEffectプロパティでドラッグ&ドロッププロパティの挙動を設定・取得できます。 |
■表1:DragEventArgesの機能
今回の例でもDragEnterイベントではコピーを許可するために、e.Effect = DragDropEffects.Copyとしています。ただし、テキストボックスですから文字列以外のもののコピーを許可するわけには行きません。そこでコピーを許可する前にドラッグされているデータが文字列に変換可能かどうか調べています。これはGetDataPresentメソッド(読み方:GetDataPresent = ゲットデータプレゼント)を使えば簡単にできます。
DragDropイベントでは既に文字列に変換可能なことがチェック済みなので安心してチェックなしで文字列としてデータを受け取れます。これにはGetDataメソッド(読み方:GetData = ゲットデータ)を使います。
GetDataPresentもGetDataもDataObject(読み方:DataObject = データオブジェクト)のメンバですが、このDataObjectにはイベントプロシージャの引数e(つまり、DragEventArgs)のDataプロパティを使ってアクセスできるようになっています。DataObjectにはこのほかにもドラッグ&ドロップのデータを制御するためにいくつかのメンバがあります。
なお、DragEnterイベント内で、コピーを許可している部分を移動を許可に変更するとドロップされたときにドラッグ元の文字列が削除されます。移動許可にするにはe.Effect = ... の行を次のように書き換えます。
e.Effect = DragDropEffects.Move |
■リスト2
次にもう少し複雑で役に立つ例としてファイルのドロップを紹介します。
まずは、ドロップされたファイル名をテキストボックスに表示するようにプログラムしてみます。といっても最初の例とさほど変わりはありません。最初の例で文字列に変換可能かどうか調べていたところを、ファイルかどうか調べるように修正して、DragDropイベントのデータの表示の仕方をちょっとだけ変えるだけです。
次のようになります。
Private Sub
TextBox1_DragEnter(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles TextBox1.DragEnter
'ドラッグされている内容が文字列型に変換可能な場合 |
Private
Sub
TextBox1_DragDrop(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles TextBox1.DragDrop
'ドロップされた内容を表示する End Sub |
■リスト3:ファイルがドロップされるとそのファイル名を表示する
先ほどの例からの変更箇所を説明します。まず、DragEnterイベントでのGetDataPresentメソッドの引数が変わりました。はじめの例ではGetType(String)となっていたのが、ここでは、DataFormats.FileDropとなっています。これは、はじめの例が「String型に変換可能かどうか」調べていたのに対し、今回は「ファイルかどうか」を調べているからです。このようにGetDataPresentは柔軟な引数と取ることができます。
.NET Framework( = VB)の型に変換できるか調べたい場合は GetType(String) のように書くことができますし、それとは別にドラッグ内容のデータを表すDataFormats列挙体(読み方:DataFormats = データフォーマッツ)を使うこともできます。(列挙体とは何かについて種類がある場合の各種類を見分けるために使うVBの手法です。)
ところで、このプログラムは最終的にはファイル名を表示するのですから文字列に変換できそうにも思えますが、あくまで「ファイルがドロップされている」という判定になりますので、e.Data.GetDataPresent(GetType(String))と記述するとFalseが返ります。
もう一箇所の変更点はDragDropイベントですが、ここではドロップされた「物」をファイルとして受け取るように、やはりDataFormats.FileDropを指定します。ファイルとして受け取った場合、具体的な扱いとしては文字列の配列になります。(配列については今後初級講座で詳しく取り上げる予定です)。
なぜ、単純な文字列にならないかというと複数のファイルをまとめてドロップする場合があるからです。ここでは、はじめの1つめのファイルだけを対象とするので (0) を指定します。
なお、2番目のファイルを対象とする場合は (1) 、3番目なら (2) のように記述します。
ドロップの最後にドロップされたファイルを開く例を紹介します。「ファイルを開く」という動作に関してはドラッグ&ドロップとは直接関係ありませんし、また今後初級講座で詳しく取り上げる予定ですが、やはりファイルがドロップされたらそのファイルを処理するところまでプログラムで書きたくなると思いますのでここでも簡単に紹介します。
まず、ドロップされたテキストファイルを開くには次のようにします。この場合、テキストボックスのプロパティはあらかじめ次のように設定しておいてください。
プロパティ | 値 | 説明 |
AllowDrop | True | ドロップを受け入れる。 |
MultiLine | True | 複数行表示できるようにする。 |
ScrollBars | Both | 縦横両方向のスクロールバーを使用する。 |
WordWrap | False | 自動的に改行しない。 |
■表2
具体的なコードは先ほどの例をほとんど同じです。DragDropイベントだけ次のように書き換えてください。
Private
Sub
TextBox1_DragDrop(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles TextBox1.DragDrop
Dim FileName
As
String FileName = e.Data.GetData(DataFormats.FileDrop)(0) Reader = New IO.StreamReader(FileName, System.Text.Encoding.GetEncoding("Shift-JIS")) TextBox1.Text = Reader.ReadToEnd Reader.Close() End Sub |
■リスト4:ドロップされたテキストファイルの内容を表示する。
今度は画像がファイルがドロップされたらその画像を表示するようにします。
この場合のプログラムもほとんど前のと同じですが、ここでは全体を紹介します。
Private Sub Form1_Load(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles MyBase.Load PictureBox1.AllowDrop = True End Sub |
Private
Sub
PictureBox1_DragEnter(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles PictureBox1.DragEnter
If
e.Data.GetDataPresent(DataFormats.FileDrop)
Then |
Private
Sub
PictureBox1_DragDrop(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.DragEventArgs)
Handles PictureBox1.DragDrop
Dim FileName As String FileName = e.Data.GetData(DataFormats.FileDrop)(0) PictureBox1.Image = Image.FromFile(FileName) End Sub |
■リスト5:ドロップされた画像ファイルを表示する
ちょっとだけドラッグ&ドロップがらみでポイントになるのはフォームのLoadイベントでPictureBox1のAllowDropプロパティをTrueにしている点です。なぜかピクチャーボックスのAllowDropプロパティはプロパティウィンドウでは変更できないので仕方なくコードで変更しています。
このようにプロパティウィンドウからはできなくてもプログラムでできることはいろいろあります。
今度はドラッグを説明します。ドロップに比べてドラッグが活躍するシーンはそれほど多くありません。
まずは、単純にテキストボックスの文字列をワードパッドにドラッグドロップでコピーしてみます。
このくらいのドラッグなら簡単です。ドラッグを開始するためにDoDragDropメソッド(読み方:DoDragDrop = ドゥードラッグドロップ)を呼び出すだけです。ただ、どのタイミングでドラッグを開始するのかが悩ましいのですが今回は単純にMouseDownイベントでドラッグを開始することにします。MouseDownイベントでドラッグを開始すると、マウスで文字列が選択できなくなるなど好ましくない現象が発生しますので実用化するときは要注意です。
プログラムは次のようになります。
Private
Sub
TextBox1_MouseDown(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles TextBox1.MouseDown TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy) End Sub |
■リスト6:単純なテキストのドラッグ
これだけで、テキストボックスから他のドラッグ&ドロップ対応アプリケーションへドロップができます。当然、VBのテキストボックス間でのドラッグ&ドロップもできます。
今度はテキストボックスからデスクトップやフォルダにドロップして、テキストをファイルとして保存する例を紹介します。
このところちょっとばかり癖があるのですが、まずはプログラムから見てください。
Private
Sub
TextBox1_MouseDown(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles TextBox1.MouseDown
Dim Data
As
New DataObject '▼ファイル作成 FileName = Application.StartupPath & "\TextBox1.txt" '▼ファイルをドラッグデータとしてセット Data.SetData(DataFormats.FileDrop, New String() {FileName}) '▼ドラッグ開始 TextBox1.DoDragDrop(Data, DragDropEffects.Copy) End Sub |
■リスト7:ドラッグドロップしてファイルに保存する
だんだん、プログラムも長くなってきました。私はどうも嫌なのですが、どうやらファイルとしてドラッグするときはドラッグを開始する時点でファイルが存在していないとダメなようなのです。普通の発想ではドロップが完了した時点で、「ドロップ完了」のようなイベントが発生して、 そのイベントの中でファイルを書き込む…のような流れだと思うのですが、違うのです。
そのため、このサンプルでもDoDragDropは一番最後で、ファイルを作るのが最初です。そのファイルもドラッグ開始の前に作成しますから、とりあえず適当な場所に作成するしかありません。ここではexeファイルと同じ場所にTextBox1.txtという名前でとりあえずファイルを作成しています。
その後の流れは初級講座の現段階では理解しなくても構わないと思いますが、一応ざっと触れておきます。まず作成したファイルをドラッグデータとしてセットするところなのですが、単なる文字列の場合と違って一旦DataObjectに設定して、その後でそのDataObjectをDoDragDropメソッドの引数に指定すると言う手順を踏まなければなりません。そして、DataObjectにデータをセットするときに、ファイルは常に文字列型の配列としてセットしなければならないので、New String() {FileName} のようなちょっと妙な指定方法となります。
時にはラベルやピクチャーボックスなどコントロールをマウスのドラッグにより移動できるようにしたい場合があるかもしれません。
特にピクチャーボックスには画像を表示できるわけですからアイディアしだいでさまざまな視覚効果のあるドラッグを実現できます。たとえば、WordやExcelではオートシェイプと呼ばれる図形をマウスでドラッグして位置を調節することができますが、ちょうどそんなものを想像していただければ結構です。
このようなコントロールのドラッグ&ドロップですが、実はいままでのドラッグ&ドロップの仕組みは使わないでマウス系のイベントを使って自分ですべてを管理するのが良いです。
次の例ではフォーム上でボタン(Button1)をドラッグして、自由に位置を変えられます。
Dim
IsDragging As Boolean
'ドラッグ中の場合True Dim DiffPoint As Point 'ドラッグ開始地点とドラッグ開始時のボタンの位置とのずれ |
Private
Sub
Button1_MouseDown(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles Button1.MouseDown
If e.Button =
MouseButtons.Left Then |
Private
Sub
Button1_MouseUp(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles Button1.MouseUp
If e.Button =
MouseButtons.Left Then |
Private Sub Button1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Button1.MouseMove If IsDragging Then Dim DestX
As
Integer =
Button1.Location.X + e.X - DiffPoint.X Button1.Location = New Point(DestX, DestY) End If End Sub |
■リスト8:ドラッグしてコントロールの位置を変更する。
少し長いコードに感じられるかもしれませんが、前回のマウス系のイベントの知識があれば特にこれといったポイントはありません。あげるとすれば、MouseMoveイベントでのButton1の位置の移動です。
ボタンの位置の指定にはLocationプロパティ(読み方:Location = ロケーション)を使用しています。Locationプロパティには新しい位置を表すPoint構造体(読み方:Point = ポイント)をセットするだけなのですが、Locationプロパティは正確にはコントロールの左上の座標を表しているため、この新しい位置を算出するときに次の3つの座標を考慮に入れる必要があります。
意味上の座標 | プログラム上の座標 | 備考 | |
A | 現在のボタンの左上の座標 | Button1.Location | |
B | 現在のマウスの座標 | e.x , e.y | この座標はボタンの左上の座標を(0, 0)としたときの相対的な座標です。 |
C | ドラッグを開始したときのマウスの座標 | DiffPoint | この座標もボタンの左上の座標を(0, 0)としたときの相対的な座標です。 |
■表3:考慮すべき2つの座標
この3つを考慮に入れると、新しい座標は A + B - C となります。このあたりを図入りでもっと丁寧に解説することも考えたのですが今回のテーマからははずれてきてしまうし、とりたてて応用があるわけでもないので結論だけ載せます。
なお、VB2005ならばMouseMoveイベントの中は次のように書くこともできます。
Private
Sub
Button1_MouseMove(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles Button1.MouseMove If IsDragging Then Dim Dest As Point Dest = Button1.Location + New Size(e.X, e.Y) - New Size(DiffPoint) Button1.Location = Dest End If End Sub |
■リスト9:VB2005ではPoint構造体とSize構造体を直接演算できる。
ドラッグとドロップの基本の説明は以上です。VBにはさらに高度なドラッグ&ドロップを実現するために今回は紹介しなかった仕組みやメソッド、プロパティが用意されています。たとえば、ドラッグ中に発生するGiveFeedBackイベント(読み方:GiveFeedBack = ギブフィードバック)は今回は紹介しませんでしたが、ドラッグ中にもっとこった視覚効果を適用することができます。
他にも画像のドラッグや、独自のデータのドラッグなどいろいろあります。ただ、こういったトピックを扱っていくと明らかに初級講座の範囲を逸脱してしまいます。興味のある方は調べてみてください。