トップ回答者
picturebox に表示される画像上でドラッグ&ドロップを行いたいです。

質問
回答
-
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
すべての返信
-
> 四角を表示しました。
こちらは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
-
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;
マウスで、長方形を移動したり、大きさを変更したりしたいのです。
よろしくお願いします。
-
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