マウス移動時の制御について(コントロール)

タグの編集
投稿者 やむちゃ  (社会人) 投稿日時 2016/10/11 20:45:13
以下のようなコードで
フォーム上に配置したテキストボックス等のコントロールを
マウスでドラッグアンドドロップ可能にしました。

これを、
①コントロールの開始位置から同じX方向か同じY方向にしか移動できないようにしたい。
(将棋の飛車のような動き)
②①のような動きで、Ctrlキーでのコピー処理をしたい。
のように改造したいのですが、行き詰っています。
どのようにすればよいか、アドバイスをお願いいたします。

Public Class Form1

Private StartPositon As Size  

Private Sub TextBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseDown

        If e.Button = System.Windows.Forms.MouseButtons.Left Then
             StartPositon = New Size(e.X, e.Y)
        End If

 End Sub

Private Sub TextBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseMove

        If e.Button = System.Windows.Forms.MouseButtons.Left Then
            
            TextBox1.Location = Point.op_Subtraction(Me.PointToClient(System.Windows.Forms.Cursor.Position), StartPositon)

        End If

End Sub

End Class
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/10/11 22:52:01
> テキストボックス等のコントロールを
テキストボックスに対するドラッグ操作は、本来ならば文字列選択のために使われるものですよね。
ドラッグ操作を組み込むなら、別のコントロールに仕掛けた方が良いと思いますよ。たとえば Label とか。


> TextBox1.Location = Point.op_Subtraction(Me.PointToClient(System.Windows.Forms.Cursor.Position), StartPositon)
VB.NET 2002/2003 をお使いでしょうか? VB2005 以降では、
op_Subtraction メソッドの代わりに - 演算子を使います。具体的にはこんな感じ。

TextBox1.Location = Me.PointToClient(System.Windows.Forms.Cursor.Position) - StartPositon



> コントロールの開始位置から同じX方向か同じY方向にしか移動できないようにしたい。
Location プロパティではなく、Left プロパティや Top プロパティを使ってみてください。

'TextBox1.Location = Me.PointToClient(System.Windows.Forms.Cursor.Position) - StartPositon 
'TextBox1.Left = (Me.PointToClient(System.Windows.Forms.Cursor.Position) - StartPositon).X 
TextBox1.Top = (Me.PointToClient(System.Windows.Forms.Cursor.Position) - StartPositon).Y


ただし今のままのコードだと、フォームの端を超えて移動できてしまいますので、
算出した座標の値を調べて、必要以上に移動されないようにしておいた方が良いでしょう。


> Ctrlキーでのコピー処理をしたい。
Ctrl が押されているかどうかは、ModifierKeys プロパティで分かります。
どの時点でコピーするのかも、仕様として定めておいた方が良いかもしれませんね。


(案1) ドラッグを開始した時点でコピーを作り、
ドラッグ中はそのコピーを移動させる。

(案2) ドラッグが完了した時点でコピーを作り、
ドロップされた位置にそのコピーを配置する。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/10/12 11:47:40
案1 で作ってみました。
古いバージョン(VB.NET 2002 など)だと使えない機能も含まれていますが御容赦を。

Ctrl を押しながら左ドラッグを開始すればコピーになりますが、
(左ドラッグを始めてから Ctrl を押すのはNG)

ついでに、ドラッグ中に[Esc]が押されたらドラッグ処理をキャンセルさせています。

Public Class Form1
 Private dragTarget As Control = Nothing

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  KeyPreview = True

  'コントロールをドラッグ可能にする 
  AddHandler TextBox1.MouseDown, AddressOf Target_MouseDown
  AddHandler Label1.MouseDown, AddressOf Target_MouseDown
  AddHandler CheckBox1.MouseDown, AddressOf Target_MouseDown
 End Sub

 Private Sub Target_MouseDown(ByVal sender As ObjectByVal e As MouseEventArgs)
  If e.Button <> System.Windows.Forms.MouseButtons.Left Then
   Return
  End If

  'ドラッグされたコントロール自身 
  Dim this As Control = DirectCast(sender, Control)

  'Ctrl が押されていたかどうか 
  Dim isCopy As Boolean = (Control.ModifierKeys And Keys.Control) = Keys.Control

  'Ctrl が押されていなければ自身をセット 
  '押されてたらコントロールを複製してセット 
  If isCopy Then
   '新しいコントロールを作成し、位置・サイズ・内容等を複製 
   dragTarget = DirectCast(Activator.CreateInstance(this.GetType()), Control)
   dragTarget.Text = this.Text
   dragTarget.Parent = this.Parent
   dragTarget.SetBounds(this.Left, this.Top, this.Width, this.Height)
   this.Capture = False
   dragTarget.Visible = True
   dragTarget.BringToFront()
   dragTarget.Focus()
   dragTarget.Capture = True
   AddHandler dragTarget.MouseDown, AddressOf Target_MouseDown
  Else
   dragTarget = this
  End If
  Cursor = Cursors.Hand   'コピーと移動でカーソルを変えるのも良いかも 

  '現在の位置情報を Tag プロパティに保存しています 
  Dim oldPos As Point = dragTarget.Location
  Dim dragOffset As Size = dragTarget.Parent.PointToClient(MousePosition) - oldPos
  dragTarget.Tag = New Object() {dragOffset, oldPos, isCopy}

  'イベントを動的に付与します 
  AddHandler dragTarget.MouseMove, AddressOf Target_MouseMove
  AddHandler dragTarget.MouseUp, AddressOf Target_MouseUp
 End Sub

 Private Sub Target_MouseMove(ByVal sender As ObjectByVal e As MouseEventArgs)
  If dragTarget Is Nothing Then
   Return
  End If

  'Tag プロパティに保存していた位置情報を復元 
  Dim target As Control = DirectCast(sender, Control)
  Dim pack() As Object = CType(target.Tag, Object())
  Dim dragOffset As Size = CType(pack(0), Size)
  Dim oldPos As Point = CType(pack(1), Point)

  '現在の位置情報 
  Dim curPos As Point = target.Parent.PointToClient(MousePosition) - dragOffset

  '縦横どちらに多く動かしたかを調べて、 
  '垂直移動と水平移動を切り替えています 
  Dim moveOffset As New Size(Math.Abs(curPos.X - oldPos.X), Math.Abs(curPos.Y - oldPos.Y))
  If moveOffset.Width = moveOffset.Height Then
   target.Location = oldPos
  ElseIf moveOffset.Width < moveOffset.Height Then
   target.Location = New Point(oldPos.X, curPos.Y)
  ElseIf moveOffset.Width > moveOffset.Height Then
   target.Location = New Point(curPos.X, oldPos.Y)
  End If
 End Sub
 Private Sub Target_MouseUp(ByVal sender As ObjectByVal e As MouseEventArgs)
  If dragTarget IsNot Nothing AndAlso e.Button = System.Windows.Forms.MouseButtons.Left Then
   Dim target As Control = DirectCast(sender, Control)
   EndDrag(target)
  End If
 End Sub

 'ドラッグを完了させる 
 Private Sub EndDrag(ByVal target As Control)
  RemoveHandler target.MouseUp, AddressOf Target_MouseUp
  RemoveHandler target.MouseMove, AddressOf Target_MouseMove
  Cursor = Cursors.Default
  target.Tag = Nothing
  dragTarget = Nothing
 End Sub

 Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
  'Esc が押されたらドラッグをキャンセル 
  If dragTarget IsNot Nothing AndAlso e.KeyCode = Keys.Escape Then
   Dim pack() As Object = CType(dragTarget.Tag, Object())
   If CBool(pack(2)) Then
    'コピー中のコントロールを破棄 
    Using dragTarget
     dragTarget.Parent.Controls.Remove(dragTarget)
     EndDrag(dragTarget)
    End Using
   Else
    '移動中のコントロールの位置を戻す 
    dragTarget.Location = CType(pack(1), Point)
    EndDrag(dragTarget)
   End If
  End If
 End Sub
End Class
投稿者 やむちゃ  (社会人) 投稿日時 2016/10/13 20:27:55
魔界の仮面弁士様
早々にご回答頂き、しかもサンプルコードまでありがとうございます。
こんなコードが短時間で書けるなんて、凄いの一言です。
素晴らしいご回答により、本件はクローズと致します。