Visual Basic 6.0 テクニック |
Visual Basic 中学校 > VB6 テクニック >
16.ドラッグ&ドロップ
今回は ドラッグ&ドロップの基本については解説します。ドラッグ&ドロップでファイルを開いたり、保存したり、文字や画像を他のアプリケーションとドラッグ&ドロップで交換したり、フォーム内でドラッグしてコントロールの位置をリアルタイムで変更したり…。ドラッグ&ドロップでプログラムの幅をぐっと広げましょう。
この回の要約 ・ドラッグ&ドロップには他のアプリケーションと連携する「OLEドラッグ&ドロップ」と、1つのアプリケーション内で完結する「ドラッグ&ドロップ」の2種類がある。 ・OLEドラッグ&ドロップを行うには、OLEDragModeプロパティやOLEDropModeプロパティを「自動」にするか、OLEDragメソッドを呼び出す。 ・OLEドロップが行われると、ドロップ側ではOLEDragDropイベントが発生する。 ・通常のドラッグ&ドロップを行うには、DragModeプロパティを自動にするか、Dragメソッドを呼び出す。ドロップ側では、ドロップされるとDragDropイベントが発生する。 |
ドラッグ&ドロップはWYSWIGという考え方の中で重要な役割を果たしています。WYSWIGは「ウィズウィグ」と読み、簡単に言うと「見た目でできそうなことは実際にできる」という考え方です。たとえば、よくみかける「ボタン」ですが、あれは実際には単なる画像なのですがいかにも「押せそうな感じ」がしますよね?そして押すことができます。これはWYSWIGです。
フォルダから別のフォルダにファイルをドロップしたら、そのファイルが移動できそうな気がしませんか?これもWYSWIGです。
もし、画像を表示するソフトでは画像ファイルがドロップされたのにその画像が表示されないとしたらユーザーはどう思うでしょうか?
みなさんのソフトにもドラッグ&ドロップを実装して親切で使いやすいソフトを目指しましょう。
さて、ユーザーの視点で見ると「ドラッグ&ドロップ」はどれも同じように見えるかもしれませんが、実は大きく分けて2種類のドラッグ&ドロップが存在し、プログラム方法も異なります。
1つは「OLEドラッグ&ドロップ」と呼ばれるもので、異なるアプリケーション間でのドラッグ&ドロップを指します。たとえば、メディアプレイヤーに音楽ファイル(mp3等)をドロップしたり、WordにExcelに文字をドラッグ&ドロップしたりするのはOLEドラッグ&ドロップです。
もう1つは同じアプリケーション内でのドラッグ&ドロップです。これは、エクセルでマウスを使ってセルを移動したり、ペイントで画像の位置を調節するために画像をドラッグしたりする動作がこれにあたります。
VBでは当然この両方を実装することができます。
今回はまず、OLEドラッグ&ドロップについて説明し、その次に同じアプリケーション内でのドラッグ&ドロップについて説明します。同じアプリケーション内でのドラッグ&ドロップについては素敵なサンプルを送ってくださった方がいらっしゃいまして、 一番最後にそのサンプルを紹介します。サンプルを元に作った福笑いゲームをダウンロードすることもできます。
驚くべきことにプロパティを1つ設定するだけで単純なOLEドラッグ&ドロップが完成してしまいます。
テキストボックスのOLEDragModeプロパティ(読み方:OLEDragMode = オーエルイードラッグモード)を「1 - 自動」にしてプログラムを実行してみてください。そして、そのテキストボックスに何か文字を入力してその文字を選択します。あとは、マウスでその文字列をドラッグすると他のウィンドウにドロップできます。
ドロップ先のウィンドウでもOLEドラッグ&ドロップに対応していなければならないのでどのウィンドウでも良いというわけではありません。たとえば、「メモ帳」にドロップすることはできません。その代わりWindows付属の「ワードパッド」はドロップ可能ですのでワードパッドにドロップしてみてください。
見事にテキストボックスの文字列がワードパッドにコピーされるでしょう。
逆も簡単です、今度はテキストボックスのOLEDropModeプロパティ(読み方:OLEDropMode = オーエルイードロップモード)を「1 - 自動」にして、ワードパッドからテキストボックスに文字をドロップしてください。こちらもちゃんと文字列がコピーされます。
PictureBoxやImageでも同じことができます。OLEDragModeプロパティやOLEDropModeプロパティを「2 - 自動」にするだけで他のOLEドラッグ&ドロップ対応ソフトとの間で画像をドラッグ&ドロップできるようになります。
なんだかこれだけで十分な気もしますが、さらにOLEドラッグ&ドロップを細かく制御するための方法も用意されています。ファイルのドラッグ&ドロップを例に説明しましょう。
今度はテキストファイルがドロップされた場合に、テキストボックスにそのファイルの内容を表示するようにしてみましょう。これは自動ではできません。まずはテキストボックスのプロパティを次のように設定してください。
プロパティ | 値 |
OLEDropMode | 1 - 手動 |
ScrollBars | 2 - 垂直 |
MultiLine | True |
■表1
準備ができたらいよいよプログラムです。
テキストボックスに何かがドロップされてくると、OLEDragDropイベントが発生するのでここにプログラムします。
ドロップされてきたものの情報はOLEDragDropイベントの第1引数Dataを見ればわかるようになっています。
Private Sub Text1_OLEDragDrop(Data
As DataObject, Effect As
Long, Button As Integer, Shift
As Integer, X As Single, Y
As Single) Dim LineText As String Dim AllText As String Dim Fn As Integer If Data.Files.Count > 0 Then Fn = FreeFile() Open Data.Files(1) For Input As #Fn Do Until EOF(Fn) Line Input #Fn, LineText AllText = AllText & LineText & vbNewLine Loop Close #Fn Text1.Text = AllText End If End Sub |
■リスト1:ドロップされたテキストファイルの内容を表示する
DataObject(読み方:DataObject = データオブジェクト)はファイルがドロップされた場合ファイル名をFilesプロパティ(読み方:Files = ファイルズ)に持っていますので、このFilesプロパティを調べればファイルを取得できます。ファイル名がわかれば後は通常のファイルの読み込み処理です。対象が画像ファイルならLoadPicture関数を実行するなどどのような処理でも書くことができます。
なお、複数のファイルが一度にドロップされたときのためにFilesプロパティはコレクションになっています。
次は、テキストボックスをフォルダにドロップしたらテキストファイルを作成するようにプログラムしてみましょう。こちらは少し複雑になってきます。
まず、テキストボックスのOLEDragModeを「0 - 手動」にしてください。これは問題ないでしょう。
問題となるのはドラッグを開始するタイミングです。OLEドラッグを開始するためにはOLEDragメソッドを呼び出す必要があるのですが、いったいどのイベントで呼び出すべきでしょうか?
この問題に関しては明快な答えはありません。状況に応じてもっとも適切なイベントを自分で探すことになります。今回は、単純にMouseDownイベントでOLEDragを開始します。実際はテキストボックスのMouseDownイベントでOLEDragを呼び出すと、マウスによる文字選択ができなくなるのど不都合がありますのでご注意ください。
OLEDragが開始されるとOLEStartDragイベント(読み方:OLEStartDrag = オーエルイースタートドラッグ)が発生します。ここではドラッグ内容がファイルであることを示すためにDataObjectのプロパティをセットします。
対象にドロップされるとOLESetDataイベント(読み方:OLESetData = オーエルイーセットデータ)が発生します。ここでは実際にファイルを作成してDataObjectに渡します。
これで完了です。後はドロップ先のアプリケーションが責任を持ちます。
以上の内容をプログラムすると次のようになります。
Private Sub Text1_MouseDown(Button
As Integer, Shift As
Integer, X As Single, Y
As Single) Text1.OLEDrag End Sub |
Private Sub Text1_OLESetData(Data
As DataObject, DataFormat As Integer) If DataFormat = vbCFFiles Then Dim FileName As String Dim Fn As Integer Fn = FreeFile() FileName = App.Path & "\Temp.txt" Open FileName For Binary As #Fn Put #Fn, , Text1.Text Close #Fn Data.Files.Add FileName Data.SetData , vbCFFiles End If End Sub |
Private Sub Text1_OLEStartDrag(Data
As DataObject, AllowedEffects
As Long) Data.SetData , vbCFFiles AllowedEffects = vbDropEffectCopy End Sub |
■リスト2:ドラッグ&ドロップしたテキストボックスをテキストファイルとして保存する
ちょっと癖があるのはOLESetDataイベントでの実際のファイルの作成部分です。実際にはどこでも良いのですが一時的にどこかにファイルを作成する必要があるようです。今回はアプリケーションの実行パスにTemp.txtと言う名前でファイルを作成しています。そしてDataObjectにはこのファイルを渡します。
そのため巨大なファイルを作成する場合ここでタイムラグが発生してドロップ先との連携に失敗する場合があります。
この他に、OLEStartDragイベントでこのドラッグによって何を行うかAllowedEffectsに指定することを忘れないで下さい。
この方式でいけば画像ファイルも簡単にドロップして作成することができます。もし、あなたが画像編集ソフトを作っているのならば是非その画像をフォルダにドロップするだけでファイルに保存できる機能をつけてください。
さて、OLEドラッグ&ドロップから離れて今度は同じフォーム内でのドラッグ&ドロップを見てみましょう。
こちらにも自動モードがありますのでまずは自動モードで簡単に試して見ます。フォームにコマンドボタンを1つ配置してDragModeプロパティ(読み方:DragMode = ドラッグモード)を「1 - 自動」にしてください。
この状態で実行して、コマンドボタンをフォーム内でドラッグしてみると、確かにドラッグできます。しかし、ドロップはできません。実はドロップの方は手動でプログラミングする必要があります。
実行をいったん終了させてからフォームのDragDropイベント(読み方:DragDrop = ドラッグドロップ)に次の通り記述してください。
Private Sub Form_DragDrop(Source
As Control, X As Single, Y
As Single) Source.Move X, Y End Sub |
■リスト3:ドロップされたときの反応
DragDropイベントはドロップ「された」側で発生することに注意してください。コマンドボタンのDragDropイベントが発生するわけではありません。
これで実行するとフォーム内でコマンドボタンを自由にドラッグ&ドロップしてその位置を変られるようになります。でも、ドロップする位置とコマンドボタンが配置される位置が少しずれているように感じませんか?これは、マウスの位置を考慮に入れていないからです。
DragDropイベントの引数XとYはドロップしたときのマウスの座標です。そして、Moveメソッドはコントロールの左上の座標を変更するメソッドですから、マウスの位置とコントロールの左上の位置のずれに相当する分ドロップ位置にずれが生じます。
ためしにドラッグするときにコマンドボタンのぎりぎり左上の角を持ってドラッグするとずれがほとんどないので意図したところにボタンが配置できることが確認できます。
この「ずれ」は結構厄介です。「ずれ」を計算して相殺すればよいように思うかもしれませんが、コントロールの左上とマウスの位置のずれを計算する方法がないのです。一見MouseDownイベントでドラッグ開始時のマウスの座標を捕まえればよいように思うかもしれませんが、なんとDragModeプロパティが「1 - 自動」になっているときはマウス系のイベントが発生しなくなってしまうのです。
これが自動モードの限界というところでしょう。考えてみれば座標は若干ずれるものの自動でこれだけできるのですからすごいじゃありませんか。
今度は手動モードにして、このずれを解消する方法を説明します。コマンドボタンのDragModeプロパティを「0 - 手動」にしてください。この状態でドラッグを開始するにはプログラムでコマンドボタンのDragメソッド(読み方:Drag = ドラッグ)を呼び出します。
ドラッグを開始するのに最適なイベントはなんと言ってもMouseDownです。ここではMouseDownイベントで単にDragメソッドを呼び出すだけではなく、ドラッグ開始時のマウスの位置とコマンドボタンの左上の位置とのずれを記録しておくことにします。このずれはそのままMouseDownイベントの引数XとYに相当しますからプログラムは次のようになります。
Dim DiffX
As Single Dim DiffY As Single Private Sub Command1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) DiffX = X DiffY = Y Command1.Drag End Sub |
■リスト4:手動モードでのドラッグ開始
これでドラッグができるようになりましたから、ドロップのほうも記述します。当然ドロップしたときの座標を補正してずれがないようにします。次の通りです。
Private Sub Form_DragDrop(Source
As Control, X As Single, Y
As Single) Source.Move X - DiffX, Y - DiffY End Sub |
■リスト5:ドロップされたときの反応
実行すると今度は快適にコマンドボタンの位置をドラッグで変えることができます。でも、これ何に使いましょう?
以上がドラッグ&ドロップの基本ですが、ドラッグ&ドロップにはさらに複雑な制御をするためのイベント等が用意されていますので興味がある方は調べてみてください。
ここでは深入りはしないで別のアプローチを紹介します。別のアプローチと言うのは手動でも自動でもVBであらかじめ用意されているドラッグ&ドロップを使用しないで、自らのプログラムによりドラッグ&ドロップを同じようなことを してコントロールの移動を実現させるアプローチです。
こう書くと難しく考えてしまうかもしれませんが、やっていることはそれほど難しくはありません。また、VBの仕組みを利用していないために柔軟なプログラミングが可能になります。たとえば、VBの方式ではドラッグするコントロールとドロップされるコントロールの両方にプログラムが必要だったためどうしても煩雑になっていしまいますが、このアプローチだとドラッグするコントロールについてのみ気にしていれば良いので楽です。
このため汎用化することも容易でした。
ところで、このアプローチによるプログラムは以前に掲示板に書き込んでいただいたMakohyuさんが送ってくれたものです。最初送ってもらったプログラムを実行したときはスムーズにドラッグができるので思わず笑ってしまいました。この後で紹介するプログラムもほとんどがMakohyuさんに送っていただいたプログラムのままです。
では、まずこのアプローチの核心となるクラスを紹介します。プロジェクトに新しくDragableImageという名前のクラスを追加して以下の通りプログラムしてください。
Option Explicit Public WithEvents Control As Image Private DDStart As Boolean Private DDSource As Control Private DDPosX As Long Private DDPosY As Long |
Private Sub DragStart(Source
As Control, X As Single, Y
As Single) Set DDSource = Source DDPosX = X DDPosY = Y DDSource.ZOrder DDStart = True End Sub |
Private Sub DragEnd() If DDStart = True Then DDStart = False Set DDSource = Nothing End If End Sub |
Private Sub MouseMoveAction(Source
As Control, Button As Integer, X
As
Single, Y As Single) If DDStart = True Then DDSource.Move (DDSource.Left + X - DDPosX), (DDSource.Top + Y - DDPosY) End If End Sub |
Private Sub Control_MouseDown(Button
As Integer, Shift As Integer, X
As
Single, Y As Single) If Button = vbLeftButton Then DragStart Control, X, Y End If End Sub |
Private Sub Control_MouseMove(Button
As Integer, Shift As Integer, X
As
Single, Y As Single) MouseMoveAction Control, Button, X, Y End Sub |
Private Sub Control_MouseUp(Button
As Integer, Shift As Integer, X
As
Single, Y As Single) DragEnd End Sub |
■リスト6:ドラッグ&ドロップによる移動をサポートする汎用クラス
このクラスはImageコントロールのドラッグ専用ですが、Imageコントロールのドラッグに関してはほとんどのシーンで使えるように汎用的なつくりになっています。 また、WithEvents部分の宣言を変更すればImage以外のコントロールに対応することも容易ですし、ちょっと工夫すればいろいろなコントロールに対応できるようにもできます。ただし、残念なことにWithEventsキーワードを使っているためにコントロール配列になっているコントロールの場合はこのクラスを利用することができません。
さて、フォームの側では仮にImageコントロールを1つ配置して何かの画像を表示させてください。あまり大きくない画像が良いでしょう。
そして、次のようにプログラムするだけでもうそのImageコントロールはドラッグ可能になります。
Dim DragableImage1
As New DragableImage Private Sub Form_Load() Set DragableImage1.Control = Image1 End Sub |
■リスト7
もし、ドラッグ可能にしたいImageコントロールがもう1つあるならば同じようにDragableImage2を宣言して、そのControlプロパティにImage2をセットするだけでドラッグ可能になります。全く便利ではありませんか。Makohyuさんに感謝です。
ところで、Makohyuさんは実は別の検証をされていまして、それはドラッグ&ドロップとは直接関係ないのですが、「ユーザーコントロールが張り付いているとドラッグする際の画像のちらつきがかなり抑えられる」ということです。実際に私もその現象を確認しました。どういう原理なにかはわかりませんが…。この場合ユーザーコントロールは最小限のプロパティだけ設定してあればよいので特にプログラムを記述する必要はありません。プロパティの設定はBackStyle = 0 (透明)、Windowless = Trueです。ユーザーコントロールを作れる環境の方は是非試してみてください。
しかし、VB6の評価版やLearning Editionを使用されている方はユーザーコントロールを作成できませんので、その場合はここに書いてあるコードだけをコピーしてドラッグ&ドロップを試してください。
最後に、この仕組みを利用して福笑いを作ってみました。もっと実用的なものが思い浮かべばよかったのですが…。まぁダウンロードして楽しんでください。
TEC16_Fukuwarai.lzh 16.7KB lha形式(拡張子lzh) VB6用ソースコード
遊ぶときは、目を閉じてここぞと思うところをクリックしてください。そこに何かあれば音で知らせてくれます。音がしたら適当な位置にドラッグしてください。この作業を繰り返して顔を完成させましょう。
今回はドラッグ&ドロップについて簡単に説明しましたが、あくまで利用法の一部を紹介したに過ぎないことに注意してください。たとえば、フォーム内でのドラッグ&ドロップではコントロールを移動させる方法に焦点をしぼって解説しましたが、ドラッグ&ドロップはコントロールを移動するためだけにあるのでありません。ドラッグ&ドロップをどのように工夫して使うかはみなさんの腕の見せ所です。ドラッグ&ドロップをたくみに組み込んで是非使いやすいアプリケーション作りに取り組んでください。
…なお、VB.NET2002以降ではドラッグ&ドロップの仕組みは大きく変わってしまいます。ドラッグ&ドロップというテクノロジー自体が変わるわけではないので大枠では同じですが、細かいイベントやメソッドによる制御方法は変わってしまいますので将来を見据えている方はVB6のドラッグ&ドロップではあまり細部にこだわらない方が良いでしょう。