none
picturebox に表示される画像上でドラッグ&ドロップを行いたいです。 RRS feed

  • 質問

  • picturebox に画像が表示し、上に四角を表示しました。

    ドラッグ&ドロップでこの四角を操作したくて、やり方がわかりません。

     教えていただけませんか。 よろしくお願いします。

    • 編集済み xiaoqishi 2014年6月11日 1:47
    2014年6月11日 1:31

回答

  • Windows Form アプリケーションを作成して、
    先の回答のコードを実行してみてもらうと移動ができることが確認できると思います。

    MouseDown イベント内と MouseMove イベント内を
    どこをドラッグされたかにより、処理分岐するとよいかと思います。
    手順としては以下の様になるかと思います。

    1. MouseDown時の矩形との HitTest() を判定した後、どこに当たっているかをチェックします。
    2. 中心に当たっている場合はMouseMove時に移動をし、
       四辺もしくは四角に当たっている場合はリサイズを行います。

    という感じに処理をすれば、可能かと思います。
    ただ、四辺と四角それぞれでリサイズの動きが若干違うため、コードを見て頂いたほうが早いかもしれませんね。
    あくまで、移動とリサイズを実装しただけですので、xiaoqishiさんの実装されているコードに合わせてコードは修正してください。

    Public Class Form1
    
        ''' <summary>
        ''' 矩形の構造情報
        ''' </summary>
        Private Class Square
            ' 淵の大きさ
            Public Const FRAME_MARGIN As Integer = 5
    
            Public Sub New()
            End Sub
    
            Public Sub New(x As Integer, y As Integer, width As Integer, height As Integer, color As Color)
                Me.X = x
                Me.Y = y
                Me.Width = width
                Me.Height = height
                Me.Color = color
            End Sub
    
            Public Property X As Integer
            Public Property Y As Integer
            Public Property Width As Integer
            Public Property Height As Integer
            Public Property Color As Color
    
            Public Sub CopyTo(target As Square)
                target.X = X
                target.Y = Y
                target.Width = Width
                target.Height = Height
                target.Color = Color
            End Sub
    
            Public Function HitTest(point As Point) As Boolean
                If X <= point.X AndAlso point.X <= X + Width AndAlso
                    Y <= point.Y AndAlso point.Y <= Y + Height Then
                    Return True
                End If
    
                Return False
            End Function
    
            Public Function HitTestFrame(point As Point) As Direction
                Dim left As Integer = X + FRAME_MARGIN
                Dim right As Integer = X + Width - FRAME_MARGIN
                Dim top As Integer = Y + FRAME_MARGIN
                Dim bottom As Integer = Y + Height - FRAME_MARGIN
    
                If point.X < X OrElse X + Width < point.X OrElse point.Y < Y OrElse Y + Height < point.Y Then
                    ' 範囲外
                    Return Direction.None
                End If
    
                If left <= point.X AndAlso point.X <= right AndAlso top <= point.Y AndAlso point.Y <= bottom Then
                    ' 淵よりも内側に当たっている場合はFalse
                    Return Direction.None
                End If
    
                Dim flag As Direction = Direction.None
                If point.X < left Then
                    flag = flag Or Direction.Left
                ElseIf right < point.X Then
                    flag = flag Or Direction.Right
                End If
    
                If point.Y < top Then
                    flag = flag Or Direction.Top
                ElseIf bottom < point.Y Then
                    flag = flag Or Direction.Bottom
                End If
    
                Return flag
            End Function
        End Class
    
        Private Enum Direction
            None = 0
            Top = 1
            Bottom = 2
            Left = 4
            Right = 8
        End Enum
    
        Private baseSquare As New Square    ' 元の位置・サイズを記憶して差分算出に利用
        Private draggedSquare As Square = Nothing   ' ドラッグ中の矩形
        Private draggedPosition As Point = Nothing  ' ドラッグの基点情報
        Private resizeDirection As Direction = Direction.None
        Private squares As New List(Of Square)  ' 全ての矩形情報
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ' 適当に矩形を生成
            squares.Add(New Square(0, 0, 100, 100, Color.Red))
            squares.Add(New Square(100, 100, 100, 100, Color.Blue))
            squares.Add(New Square(200, 200, 100, 100, Color.Green))
        End Sub
    
        Private Sub PictureBox1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            ' 全ての矩形を描画する
            For Each sq In squares
                Using p As New Pen(sq.Color, Square.FRAME_MARGIN)
                    e.Graphics.DrawRectangle(p, sq.X, sq.Y, sq.Width, sq.Height)
                End Using
            Next
        End Sub
    
        Private Sub PictureBox1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
            ' 既にドラッグ中の場合は処理をしない
            If draggedSquare IsNot Nothing Then Return
    
            ' 全ての矩形をチェックする
            For Each square In squares
                ' 矩形内をクリックしているかどうかの判定
                If Not square.HitTest(e.Location) Then
                    Continue For 
                End If
    
                resizeDirection = square.HitTestFrame(e.Location)
    
                ' クリックされた場所とオブジェクトを保持してReturn
                draggedSquare = square
                draggedPosition = e.Location
                square.CopyTo(baseSquare)   ' 変更前の値を記憶
                Return
            Next
        End Sub
    
        Private Sub PictureBox1_MouseMove(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
            If draggedSquare Is Nothing Then
                ' 矩形内にマウスがあるかチェック
                For Each square In squares
                    If Not square.HitTest(e.Location) Then
                        Continue For
                    End If
    
                    ' 淵に対してのあたり判定をチェック
                    Dim dir = square.HitTestFrame(e.Location)
    
                    ' マウスカーソルの変更
                    If dir = Form1.Direction.None Then
                        Me.Cursor = Cursors.Hand
                        Return
                    End If
    
                    ' マウスカーソルの変更
                    If dir = Direction.Top OrElse dir = Direction.Bottom Then
                        Me.Cursor = Cursors.SizeNS
                    ElseIf dir = Direction.Left OrElse dir = Direction.Right Then
                        Me.Cursor = Cursors.SizeWE
                    ElseIf dir = (Direction.Left Or Direction.Top) OrElse dir = (Direction.Right Or Direction.Bottom) Then
                        Me.Cursor = Cursors.SizeNWSE
                    ElseIf dir = (Direction.Left Or Direction.Bottom) OrElse dir = (Direction.Right Or Direction.Top) Then
                        Me.Cursor = Cursors.SizeNESW
                    End If
    
                    Return
                Next
    
                ' なければ通常のカーソルに戻す
                Me.Cursor = Cursors.Default
            Else
                ' ドラッグ中
                ' 差を算出
                Dim diffX As Integer = e.Location.X - draggedPosition.X
                Dim diffY As Integer = e.Location.Y - draggedPosition.Y
    
                If resizeDirection = Direction.None Then
                    ' 位置情報を補正
                    draggedSquare.X += diffX
                    draggedSquare.Y += diffY
    
                    ' 基点を更新
                    draggedPosition = e.Location
    
                Else
                    ' 座標とサイズの調整処理
    
                    If (resizeDirection And Direction.Top) = Direction.Top Then
                        draggedSquare.Y = baseSquare.Y + diffY
                        draggedSquare.Height = baseSquare.Height - diffY
                    End If
    
                    If (resizeDirection And Direction.Bottom) = Direction.Bottom Then
                        draggedSquare.Height = baseSquare.Height + diffY
                    End If
    
                    If (resizeDirection And Direction.Right) = Direction.Right Then
                        draggedSquare.Width = baseSquare.Width + diffX
                    End If
    
                    If (resizeDirection And Direction.Left) = Direction.Left Then
                        draggedSquare.X = baseSquare.X + diffX
                        draggedSquare.Width = baseSquare.Width - diffX
                    End If
    
                End If
    
                ' 再描画
                PictureBox1.Invalidate()
            End If
    
        End Sub
    
        Private Sub PictureBox1_MouseUp(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
            ' ドラッグをやめたら解放
            Release()
        End Sub
    
        Private Sub PictureBox1_MouseLeave(sender As System.Object, e As System.EventArgs) Handles PictureBox1.MouseLeave
            ' 画面外に出ても解放
            Release()
        End Sub
    
        Private Sub Release()
            ' 解放処理は複数個所で使用するのでメソッド化
            draggedSquare = Nothing
            draggedPosition = Nothing
            resizeDirection = Direction.None
        End Sub
    
    End Class
    

    Form1 に PictureBox1 を置いて、上記コードを丸ごとペーストすれば動作するはずです。
    やっつけで書いたので汚いコードになってしまいお恥ずかしいのですが、参考になれば幸いです。

    • 回答としてマーク xiaoqishi 2014年6月11日 7:07
    2014年6月11日 6:36

すべての返信

  • > 四角を表示しました。
    こちらはPaintイベントなどで手動描画されたということでしょうか?

    > この四角を操作したくて
    操作っていうのは移動させるってことでしょうか?

    簡単に考え方を書いてみます。

    1. 矩形や画像の描画情報(座標や大きさ)を構造化します。
    2. PictureBox の MouseDown イベントなどで矩形とマウスが重なっているかを判定します。
    3. 重なっていたら 掴まれた矩形のオブジェクト情報とMouseDownされた座標をそれぞれPrivate変数などに保持します。
    4. PictureBox の MouseMove イベントなどでMouseDownの座標との誤差を掴まれた矩形オブジェクトに反映させ、PictureBoxのInvalidate()します。
    5. PictureBox の MouseUp イベントなどで(3)で保持した情報を空にします。

    パッと思いつくのはこんな手順でしょうか?

    基本的なコードを書いてみました。
    Form1 の中に PictureBox1 がある前提です。

    Public Class Form1
    
        ''' <summary>
        ''' 矩形の構造情報
        ''' </summary>
        Private Class Square
            Public Sub New()
    
            End Sub
    
            Public Sub New(x As Integer, y As Integer, width As Integer, height As Integer, color As Color)
                Me.X = x
                Me.Y = y
                Me.Width = width
                Me.Height = height
                Me.Color = color
            End Sub
    
            Public Property X As Integer
            Public Property Y As Integer
            Public Property Width As Integer
            Public Property Height As Integer
            Public Property Color As Color
    
            Public Function HitTest(point As Point) As Boolean
                If X <= point.X AndAlso point.X <= X + Width AndAlso
                    Y <= point.Y AndAlso point.Y <= Y + Height Then
                    Return True
                End If
    
                Return False
            End Function
        End Class
    
        Private draggedSquare As Square = Nothing   ' ドラッグ中の矩形
        Private draggedPosition As Point = Nothing  ' ドラッグの基点情報
        Private squares As New List(Of Square)  ' 全ての矩形情報
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ' 適当に矩形を生成
            squares.Add(New Square(0, 0, 100, 100, Color.Red))
            squares.Add(New Square(100, 100, 100, 100, Color.Blue))
            squares.Add(New Square(200, 200, 100, 100, Color.Green))
        End Sub
    
        Private Sub PictureBox1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            ' 全ての矩形を描画する
            For Each square In squares
                Using brush As New SolidBrush(square.Color)
                    e.Graphics.FillRectangle(brush, square.X, square.Y, square.Width, square.Height)
                End Using
            Next
        End Sub
    
        Private Sub PictureBox1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
            ' 既にドラッグ中の場合は処理をしない
            If draggedSquare IsNot Nothing Then Return
    
            ' 全ての矩形をチェックする
            For Each square In squares
                ' 矩形内をクリックしているかどうかの判定
                If square.HitTest(e.Location) Then
                    ' クリックされた場所とオブジェクトを保持してReturn
                    draggedSquare = square
                    draggedPosition = e.Location
                    Return
                End If
            Next
        End Sub
    
        Private Sub PictureBox1_MouseMove(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
            ' ドラッグ中でない場合は処理しない
            If draggedSquare Is Nothing Then Return
    
            ' 差を算出
            Dim diffX As Integer = e.Location.X - draggedPosition.X
            Dim diffY As Integer = e.Location.Y - draggedPosition.Y
    
            ' 位置情報を補正
            draggedSquare.X += diffX
            draggedSquare.Y += diffY
    
            ' 基点を更新
            draggedPosition = e.Location
    
            ' 再描画
            PictureBox1.Invalidate()
        End Sub
    
        Private Sub PictureBox1_MouseUp(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
            ' ドラッグをやめたら解放
            Release()
        End Sub
    
        Private Sub PictureBox1_MouseLeave(sender As System.Object, e As System.EventArgs) Handles PictureBox1.MouseLeave
            ' 画面外に出ても解放
            Release()
        End Sub
    
        Private Sub Release()
            ' 解放処理は複数個所で使用するのでメソッド化
            draggedSquare = Nothing
            draggedPosition = Nothing
        End Sub
    
    End Class
    

    • 編集済み Kurogo_BB 2014年6月11日 4:22
    2014年6月11日 4:09
  • Kurogo_BBさん

     ご回答、ありがとうございます。

     質問自体が記載の不十分で、申し訳ございません。

     C#で四角(枠のみ、長方形のイメージ)表示しました

    コードの一部です。

        Bitmap canvas = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height);

    Graphics g = Graphics.FromImage(canvas);

      g.DrawRectangle(pen, 0, 0, 100, 130);

    g.Dispose();
                    
            this.pictureBox1.Image = canvas;


    マウスで、長方形を移動したり、大きさを変更したりしたいのです。

    よろしくお願いします。

    2014年6月11日 4:53
  • Windows Form アプリケーションを作成して、
    先の回答のコードを実行してみてもらうと移動ができることが確認できると思います。

    MouseDown イベント内と MouseMove イベント内を
    どこをドラッグされたかにより、処理分岐するとよいかと思います。
    手順としては以下の様になるかと思います。

    1. MouseDown時の矩形との HitTest() を判定した後、どこに当たっているかをチェックします。
    2. 中心に当たっている場合はMouseMove時に移動をし、
       四辺もしくは四角に当たっている場合はリサイズを行います。

    という感じに処理をすれば、可能かと思います。
    ただ、四辺と四角それぞれでリサイズの動きが若干違うため、コードを見て頂いたほうが早いかもしれませんね。
    あくまで、移動とリサイズを実装しただけですので、xiaoqishiさんの実装されているコードに合わせてコードは修正してください。

    Public Class Form1
    
        ''' <summary>
        ''' 矩形の構造情報
        ''' </summary>
        Private Class Square
            ' 淵の大きさ
            Public Const FRAME_MARGIN As Integer = 5
    
            Public Sub New()
            End Sub
    
            Public Sub New(x As Integer, y As Integer, width As Integer, height As Integer, color As Color)
                Me.X = x
                Me.Y = y
                Me.Width = width
                Me.Height = height
                Me.Color = color
            End Sub
    
            Public Property X As Integer
            Public Property Y As Integer
            Public Property Width As Integer
            Public Property Height As Integer
            Public Property Color As Color
    
            Public Sub CopyTo(target As Square)
                target.X = X
                target.Y = Y
                target.Width = Width
                target.Height = Height
                target.Color = Color
            End Sub
    
            Public Function HitTest(point As Point) As Boolean
                If X <= point.X AndAlso point.X <= X + Width AndAlso
                    Y <= point.Y AndAlso point.Y <= Y + Height Then
                    Return True
                End If
    
                Return False
            End Function
    
            Public Function HitTestFrame(point As Point) As Direction
                Dim left As Integer = X + FRAME_MARGIN
                Dim right As Integer = X + Width - FRAME_MARGIN
                Dim top As Integer = Y + FRAME_MARGIN
                Dim bottom As Integer = Y + Height - FRAME_MARGIN
    
                If point.X < X OrElse X + Width < point.X OrElse point.Y < Y OrElse Y + Height < point.Y Then
                    ' 範囲外
                    Return Direction.None
                End If
    
                If left <= point.X AndAlso point.X <= right AndAlso top <= point.Y AndAlso point.Y <= bottom Then
                    ' 淵よりも内側に当たっている場合はFalse
                    Return Direction.None
                End If
    
                Dim flag As Direction = Direction.None
                If point.X < left Then
                    flag = flag Or Direction.Left
                ElseIf right < point.X Then
                    flag = flag Or Direction.Right
                End If
    
                If point.Y < top Then
                    flag = flag Or Direction.Top
                ElseIf bottom < point.Y Then
                    flag = flag Or Direction.Bottom
                End If
    
                Return flag
            End Function
        End Class
    
        Private Enum Direction
            None = 0
            Top = 1
            Bottom = 2
            Left = 4
            Right = 8
        End Enum
    
        Private baseSquare As New Square    ' 元の位置・サイズを記憶して差分算出に利用
        Private draggedSquare As Square = Nothing   ' ドラッグ中の矩形
        Private draggedPosition As Point = Nothing  ' ドラッグの基点情報
        Private resizeDirection As Direction = Direction.None
        Private squares As New List(Of Square)  ' 全ての矩形情報
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ' 適当に矩形を生成
            squares.Add(New Square(0, 0, 100, 100, Color.Red))
            squares.Add(New Square(100, 100, 100, 100, Color.Blue))
            squares.Add(New Square(200, 200, 100, 100, Color.Green))
        End Sub
    
        Private Sub PictureBox1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            ' 全ての矩形を描画する
            For Each sq In squares
                Using p As New Pen(sq.Color, Square.FRAME_MARGIN)
                    e.Graphics.DrawRectangle(p, sq.X, sq.Y, sq.Width, sq.Height)
                End Using
            Next
        End Sub
    
        Private Sub PictureBox1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
            ' 既にドラッグ中の場合は処理をしない
            If draggedSquare IsNot Nothing Then Return
    
            ' 全ての矩形をチェックする
            For Each square In squares
                ' 矩形内をクリックしているかどうかの判定
                If Not square.HitTest(e.Location) Then
                    Continue For 
                End If
    
                resizeDirection = square.HitTestFrame(e.Location)
    
                ' クリックされた場所とオブジェクトを保持してReturn
                draggedSquare = square
                draggedPosition = e.Location
                square.CopyTo(baseSquare)   ' 変更前の値を記憶
                Return
            Next
        End Sub
    
        Private Sub PictureBox1_MouseMove(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
            If draggedSquare Is Nothing Then
                ' 矩形内にマウスがあるかチェック
                For Each square In squares
                    If Not square.HitTest(e.Location) Then
                        Continue For
                    End If
    
                    ' 淵に対してのあたり判定をチェック
                    Dim dir = square.HitTestFrame(e.Location)
    
                    ' マウスカーソルの変更
                    If dir = Form1.Direction.None Then
                        Me.Cursor = Cursors.Hand
                        Return
                    End If
    
                    ' マウスカーソルの変更
                    If dir = Direction.Top OrElse dir = Direction.Bottom Then
                        Me.Cursor = Cursors.SizeNS
                    ElseIf dir = Direction.Left OrElse dir = Direction.Right Then
                        Me.Cursor = Cursors.SizeWE
                    ElseIf dir = (Direction.Left Or Direction.Top) OrElse dir = (Direction.Right Or Direction.Bottom) Then
                        Me.Cursor = Cursors.SizeNWSE
                    ElseIf dir = (Direction.Left Or Direction.Bottom) OrElse dir = (Direction.Right Or Direction.Top) Then
                        Me.Cursor = Cursors.SizeNESW
                    End If
    
                    Return
                Next
    
                ' なければ通常のカーソルに戻す
                Me.Cursor = Cursors.Default
            Else
                ' ドラッグ中
                ' 差を算出
                Dim diffX As Integer = e.Location.X - draggedPosition.X
                Dim diffY As Integer = e.Location.Y - draggedPosition.Y
    
                If resizeDirection = Direction.None Then
                    ' 位置情報を補正
                    draggedSquare.X += diffX
                    draggedSquare.Y += diffY
    
                    ' 基点を更新
                    draggedPosition = e.Location
    
                Else
                    ' 座標とサイズの調整処理
    
                    If (resizeDirection And Direction.Top) = Direction.Top Then
                        draggedSquare.Y = baseSquare.Y + diffY
                        draggedSquare.Height = baseSquare.Height - diffY
                    End If
    
                    If (resizeDirection And Direction.Bottom) = Direction.Bottom Then
                        draggedSquare.Height = baseSquare.Height + diffY
                    End If
    
                    If (resizeDirection And Direction.Right) = Direction.Right Then
                        draggedSquare.Width = baseSquare.Width + diffX
                    End If
    
                    If (resizeDirection And Direction.Left) = Direction.Left Then
                        draggedSquare.X = baseSquare.X + diffX
                        draggedSquare.Width = baseSquare.Width - diffX
                    End If
    
                End If
    
                ' 再描画
                PictureBox1.Invalidate()
            End If
    
        End Sub
    
        Private Sub PictureBox1_MouseUp(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
            ' ドラッグをやめたら解放
            Release()
        End Sub
    
        Private Sub PictureBox1_MouseLeave(sender As System.Object, e As System.EventArgs) Handles PictureBox1.MouseLeave
            ' 画面外に出ても解放
            Release()
        End Sub
    
        Private Sub Release()
            ' 解放処理は複数個所で使用するのでメソッド化
            draggedSquare = Nothing
            draggedPosition = Nothing
            resizeDirection = Direction.None
        End Sub
    
    End Class
    

    Form1 に PictureBox1 を置いて、上記コードを丸ごとペーストすれば動作するはずです。
    やっつけで書いたので汚いコードになってしまいお恥ずかしいのですが、参考になれば幸いです。

    • 回答としてマーク xiaoqishi 2014年6月11日 7:07
    2014年6月11日 6:36
  • Kurogo_BBさん

     ご返信ありがとうございました。

     上記のコードでVBで動きを確認しました。

     C#に変換してみます。

     大変助かりました。

     どうもありがとうございました。

     

    2014年6月11日 6:52
  • カテゴリーが Visual Basic になっていましたので、VBで記述しましたが、C#だったんですね(汗)

    大変失礼いたしました。

    2014年6月11日 7:31
  • 質問時にカテゴリーを変更しませんでした。

    こちらこそ失礼しました。

    どうぞよろしくお願いします。

    2014年6月12日 0:30
  • Kurogo_BBさん

    解決できました。

    どうもありがとうございました。

    • 編集済み xiaoqishi 2014年6月12日 4:17
    2014年6月12日 2:34