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

コントロールをマウス移動する際にグリッドスナップしても連続してドラッグしたい

環境/言語:[XP XP3/VB2008Express]
分類:[.NET]

お世話様です。シマウマです。

コントロールをマウスで移動させて、グリッドにスナップするような
プログラムを組んだのですが、MouseMoveでスナップ移動後すると、
マウスの制御から外れてしまって、マウスを一度マウスアップして
再度ドラッグ開始させないと次の座標に移動できません。

感覚としては、グリッド移動してもマウスを連続的にドラッグ移動
できるようにしたいのですが、どうればいいでしょうか。

ご存じ方よろしくご指導願います。
あなたが書いているコードが悪さをしている可能性があるので、もう少し具体的な実装内容を出せないでしょうか。
そのものを出せなくても、再現させるサンプルコードが提供されていると、問題を指摘しやすいかと思います。
■No30973に返信(Azuleanさんの記事)

フォームモジュールに以下の様なコードを記述しています。
フォームには、ピクチャーボックスが配置してあります。
そのピクチャーボックスをグリッドスナップさせようとしています。
よろしくお願いします。

    Private Pic1Left As Long
    Private Pic1Top As Long
    Private ptDrag As Point


    Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        Pic1Left = PictureBox1.Left
        Pic1Top = PictureBox1.Top
        ptDrag = PictureBox1.PointToClient(Control.MousePosition)            'マウスダウンした時のカーソル位置を取得する
    End Sub


    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        Dim pic2 As PictureBox
        Dim Targetpt As Point
        Dim pt As Point

        If e.Button = Windows.Forms.MouseButtons.Left Then
            pic2 = DirectCast(sender, PictureBox)                            '移動するピクチャーボックスをpic2として
            pt = PictureBox1.Parent.PointToClient(Control.MousePosition)    '現在のカーソル位置を取得する
            Targetpt = pt - ptDrag
            If pic2.Left > (Pic1Left + (pic2.Width \ 2)) Then                '半分以上移動した時のみ、幅分移動した位置に設定する
                pic2.Left = Pic1Left + pic2.Width
            ElseIf pic2.Left < (Pic1Left - (pic2.Width \ 2)) Then            '半分以上移動した時のみ、幅分移動した位置に設定する
                pic2.Left = Pic1Left - pic2.Width
            Else
                pic2.Left = Targetpt.X                                        '以外は元の位置に戻す
            End If

            If pic2.Top > (Pic1Top + (pic2.Height \ 2)) Then                '半分以上移動した時のみ、高さ分移動した位置に設定する
                pic2.Top = Pic1Top + pic2.Height
            ElseIf pic2.Top < (Pic1Top - (pic2.Height \ 2)) Then            '半分以上移動した時のみ、高さ分移動した位置に設定する
                pic2.Top = Pic1Top - pic2.Height
            Else
                pic2.Top = Targetpt.Y                                        '以外は元の位置に戻す
            End If
        End If
    End Sub
ドラッグアンドドロップ系のイベントで書いたほうがいいんじゃないかなあ。
■No30974に返信(シマウマさんの記事)
> フォームモジュールに以下の様なコードを記述しています。
> フォームには、ピクチャーボックスが配置してあります。
> そのピクチャーボックスをグリッドスナップさせようとしています。
> よろしくお願いします。

幅 or 高さの半分以上移動したら、それ以上動かさないという処理を書いているのですから、当然では?
実際、If 文で見ているのは、Targetpt ではなく、pic2.Left/Top なので現在位置が条件を満たせば、後は動かなくなるでしょう。

「マウスの制御が外れる」という見方は正しくなく、「スナップ処理の不具合」です。

あとはどうしたいかによりますが、こんな単純な If 文ではだめなような気がします…。条件分岐をもう一度整理してみてください。
(幅 or 高さの半分以上移動した後、さらに移動し続けた場合の分岐はどうなるべきかなど)
ちょうど近頃、C#で似たような動作を書いたので、参考までに。

▼発生したバグ
・1回の移動で、吸いつく距離以上を動かさないと吸いつきから離れなくなった。
・(ドラッグ後)ドロップする際に移動する距離がズレた
・ゆっくりマウス位置をズラした場合に、マウスポインタが全く動かなかったり、おかしな位置に行ってしまった

▼対処した方法
・距離のズレは、ただの計算ミス(移動量はマウスポインタの初期位置から計算し、実際に移動させるのはピクチャーボックスなので、移動量を加算後再計算する必要がある)
・吸いつかせる動作(スナップ)処理は、マウスポインタの移動している向きorマウスポインタがスナップ範囲にいる時間によって行うかどうかチェックした

Azulean様のおっしゃる通り、かなりif文が複雑になりました。
多分見たところのサンプルでは、ピクチャーボックスのサイズが大きすぎると、
吸いつく範囲が広くなりすぎて、まったく動かなくなるんじゃないかな…。

仮に、(pic2.Width \ 2)の部分を、2ピクセルとかの定数でやってみると、
やろうとしていることはなんとなくできる気がしますが、
ピクセル数が多ければ多いほど、何も動かなくなってしまうかと…。

一応、MouseDownでドラッグ開始、MouseMoveでマウスポインタの位置計算、MouseUpでドロップ&ピクチャーボックスの位置計算といった流れでできると思いますよ。

もっとも、私がやったのはグリッドを表示しての物でしたが…、
参考になれば幸いです。
自分で書いておいてミスりました。

>もっとも、私がやったのはグリッドを表示しての物でしたが…、
これ、グリッドスナップって書いてあるんですし、
多分少なくともドラッグ中は線が表示されているんでしょうね。
汲み取れておりませんでした。すみません。
■No30979に返信(howlingさんの記事)

お世話になっております。シマウマです。

半日悪戦苦闘している間に、ありがたいご指摘が増えておりまして
感謝に絶えません。

半日の苦労の結果は、2×2のマトリックスで連続スナップしてくれる
ものができただけでした。
汎用性があるところまで辿りつけておりませんので、あえて
載せる程度のものでもなく申し訳ありません。

最初のコードの公開が恥ずかしくなるような、複雑な判定と
多数のフラグが必要不可欠であるということが身にしみてわかりました。

howlingがおっしゃるように、初期化位置記憶、方向判定や移動量判定、
強制移動時(スナップ)判定は必須と感じました。

ちまたでなにげに提供されているもので、実装の難易度が高いものは
結構あるもんだと感じた次第です。

完成コードの公開まで辿りつけていませんが、これで解決とさせて
いただきます。

皆様、ご指摘ありがとうございました。
またよろしくお願い致します。
解決済み!

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