DOBON.NET DOBON.NETプログラミング掲示板過去ログ

マウスドラッグによるフォームのスクロール

環境/言語:[OS : Windows XP Professional / 言語 : Visual Basic .NET / .NET Framework : 1.1]
分類:[.NET]

【解決したい問題】

はじめまして。

VB.NET 2002で開発しております。

早速ですが、Form1からForm2を表示します。
Form2はピクチャボックスが24個ありデフォルトでは
全てを表示しきれないため下の部分が見えません。
マウスホイールなどでスクロールすることはできます。

仕様上、Form1のピクチャボックスからドラッグ&ドロップでForm2のピクチャボックスに画像コピーできるのですが、その際にForm2の見えない箇所にあるピクチャボックスにドラッグしたいと思いForm2の下部までドラッグしても自動でスクロールしてくれません。
どのようにすればよいのでしょうか?

宜しくお願い致します。

【解決するために何をしたか】

初心者のためいろいろなサイトでサンプルや方法を探してみたのですがどこにも解決策が見つかりませんでした。
マウスでスクロールできているということでAutoScroll=Trueと仮定します。

まず、スクロールされた状態、つまり、位置をずらすということでは
・OnScroll メソッドでイベントをシミュレートする
または
・SetDisplayRectLocation メソッドで表示位置をずらす
という方法が使えそうです。

で、スクロールさせなければいけない状態の判断ですが、次の二つを自分で決めてドラッグ中の処理でおこなうことになると思います。
・自動スクロールをおこなう範囲>フォームの端から16Pixelの間とか
・その領域上でポインタを何秒間静止させたら自動スクロールさせるか
まどかさん

ヒントありがとうございました。

とりあえずの結果として出来ました。
※初心者なので無駄なコーディングもあるかもしれません。

'方法は
Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
PictureBox2.AllowDrop = True
Timer1.Interval = 50
End Sub

'常にForm2のサイズを取得
Public tSize As System.Drawing.Size
Private Sub Form2_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.SizeChanged
tSize = Me.ClientSize
End Sub

'下記のように4つの分岐を作りました。
Private Sub Form2_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles MyBase.DragOver
Dim X As Integer
Dim Y As Integer
Dim Pos As Point = Me.PointToClient(Windows.Forms.Cursor.Position)
X = Pos.X
Y = Pos.Y
If Y >= tSize.Height - 10 Then
Timer1.Start()
ElseIf X >= tSize.Width - 10 Then
Timer2.Start()
ElseIf Y <= 10 Then
Timer3.Start()
ElseIf X <= 10 Then
Timer4.Start()
Else
End If
End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Me.AutoScrollPosition = New Point(-Me.AutoScrollPosition.X, -Me.AutoScrollPosition.Y + 10)
End Sub

Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
Me.AutoScrollPosition = New Point(-Me.AutoScrollPosition.X + 10, -Me.AutoScrollPosition.Y)
End Sub

Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
Me.AutoScrollPosition = New Point(-Me.AutoScrollPosition.X, -Me.AutoScrollPosition.Y - 10)
End Sub

Private Sub Timer4_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer4.Tick
Me.AutoScrollPosition = New Point(-Me.AutoScrollPosition.X - 10, -Me.AutoScrollPosition.Y)
End Sub

'ドラッグターゲット外でドロップしたら
Private Sub Form2_DragLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.DragLeave
Timer1.Stop()
Timer2.Stop()
Timer3.Stop()
Timer4.Stop()
End Sub

としました。
しかしここで思ったのですが、Form2のみでドラッグスクロールをしたい場合はどうするのかな?と。
Form1からのピクチャのドラッグではなく、Form2のみでマウスをドラッグしForm下部までドラッグしたい場合のスクロールについて。

この場合を考慮しForm2にダミーのドラッグイベントを発生させてみたのですがこんなんでいいのでしょうか?
非常にスマートでない方法ですよね?

Private Sub Form2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
If e.Button = MouseButtons.Left Then
Dim fileNames As String
Dim dataObj As New DataObject(DataFormats.FileDrop, fileNames)
'実際にはこのPictureBoxはVisibleをFalseにする。
PictureBox1.DoDragDrop(dataObj, DragDropEffects.Scroll)
End If
End Sub

結果思ったと通りには動作していますが、個人的にいまいちこれで良いのか納得がいきませんでした。
周りにプログラムをする人が全くいなく通常どうするのかが分かりません。

再度ヒントを頂けましたら幸いです。

宜しくお願い致します。
■No21569に返信(たかさんの記事)
> しかしここで思ったのですが、Form2のみでドラッグスクロールをしたい場合はどうするのかな?と。
> Form1からのピクチャのドラッグではなく、Form2のみでマウスをドラッグしForm下部までドラッグしたい場合のスクロールについて。

ドラッグですのでドラッグしているオブジェクトに関係なく
ドラッグ中であれば対象のControlでDragEnter、DragLeave、DragOverが発生します。
したがって、「Form1から」に関係なくDragOverイベントに処理を記述すればよいです。

が、しかし、

> この場合を考慮しForm2にダミーのドラッグイベントを発生させてみたのですがこんなんでいいのでしょうか?

DoDragDropはドラッグ操作中状態を開始するというメソッドです。
つまり、仕様としてドラッグ可能なオブジェクトか?などに基づき意識しておこなうメソッドです。
単にスクロールさせたいのであれば現在AutoScrollによるスクロールバーが表示されているわけですから、そのために必要な処理は何もありません。

スクロールバー以外の手段も提供したいということであれば、「こうすればスクロールできる」ということをユーザーが容易に判断できなければなりません。
その意味でドラッグという手法は適切ではありません。
物をドラッグして何ぼですし、PictureBoxを対象にしてもドロップがおこなわれないドラッグになってしまいます。

あと、タイマですが、確かに何秒後に一度だけ処理するという使い方をすることがありますので問題ありませんが
止めるべき時に止めているか、不要なときに動いていないかを確認してください。

たぶん、DragOverのタイマをスタートしている部分で位置の調整をおこなえばタイマは不要になるはずです。
その場合、スクロールすべき領域に初めて入ったときに時間を保存して、保存した時間と現在時間を比較し「何秒以上経過していたらスクロール」とすれば、開始までのタイムラグを作れます。
まどかさん
ありがとうございます。

Form2自体のドラッグというのは確かにおかしいですね。
これは私の勘違いでした。


また、ドラッグ後スクロール領域に入った時の時間と
現在の時間の差異がX秒以上という感じの記述をしてみたのですが
うまくいきませんでした。
いろいろ試したなかで、下記のような記述でForm1、Form2起動後の一回目のドラッグでは正常に動作するのですが
2回目以降は指定した時間のラグを認識しません。
おそらく保存時間がクリアされず、現在の時刻と3秒以上経ったままなので
即座にスクロールしていると思われます。
Now2 = Nothingなどの記述ではだめなのでしょうかね。
素人ですみません。

いろいろとサイトを探したのですがこれ以上わかりませんでした。


Public NowTime As Date = Now
Public Now2 As DateTime

Private Sub Form2_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles MyBase.DragOver
Dim X As Integer
Dim Y As Integer
Dim Pos As Point = Me.PointToClient(Windows.Forms.Cursor.Position)
X = Pos.X
Y = Pos.Y

If Y >= tSize.Height - 10 Then
Now2 = DateTime.Now.ToString()
If 3 < Now2.Subtract(NowTime).Seconds.ToString() Then
Me.AutoScrollPosition = New Point(-Me.AutoScrollPosition.X, -Me.AutoScrollPosition.Y + 1)
End If

ElseIf X >= tSize.Width - 10 Then

結果報告とさせて頂きます。
経過時間の開始値は外から中へ入った初めてのときに再設定する必要があります。


目的に至るまでの条件を前提度の強い順にまずは整理すると良いです。

If文が深くなりわかりづらくなったら、ガード句というやり方がありますので活用してみてください。

If a <=0 Then Exit Sub

If a < 3 Then

この場合、最初の条件でゼロ以下を除外しています。
それ以降は1以上だという暗黙の前提で記述できます。
まどかさん、いろいろとありがとうございました。

タイムラグはまだ出来ませんが、かならず習得します。
現在の動作で特に不都合がないと思いましたので
今回はこれでタイムラグなしでいってみようと思います。

いろいろとありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板