テキストのドラッグ&ドロップによるムーブ

タグの編集
投稿者 kmkm  (社会人) 投稿日時 2009/9/9 06:29:53
TextBox1から2にドラッグ&ドロップで文字列をムーブしたいと思って、
以下のようにしたのですが、コピーと同じように、TextBox1に文字列が
残ったままとなってしまいます。
ムーブなので、元の文字列(TextBox1.Text)が消えてほしいのです。
どのようにすればよいのでしょうか。

ちなみに、ワードパッドのテキストは、TextBox2にドロップすると、
ちゃんとワードパッド側で消えてくれます。

    Private Sub TextBox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop
        TextBox2.Text = e.Data.GetData(GetType(String))
    End Sub

    Private Sub TextBox2_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TextBox2.DragEnter
        If e.Data.GetDataPresent(GetType(String)) Then
            'e.Effect = DragDropEffects.Copy
           e.Effect = DragDropEffects.Move
        End If
    End Sub

    Private Sub TextBox1_MouseDown(ByVal sender As Object, ByVal e As    System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseDown
       'TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy)
        TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Move)
    End Sub

なお、環境はVista、VB2008EEです。
投稿者 まだまだ  (中学生) 投稿日時 2009/9/9 07:50:57
強引ですが、TextBox2_DragDropを

    Private Sub TextBox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop
        TextBox2.Text = e.Data.GetData(GetType(String))
        TextBox1.Text = ""
    End Sub

にしたらどうでしょうか?
投稿者 kmkm  (社会人) 投稿日時 2009/9/9 07:59:31
まだまださん。ありがとうございます。
しかし、うーん。それはちょっと強引ですね。
TextBox1から2というのは
あくまで例示と考えていただけると幸いです。
TextBox3からかもしれませんし、TextBox4からかもしれません。
さらに、たとえば、ワードパッドの方に、ドラッグ&ドロップしても、
ムーブなんですからTextBox1のテキストが消えてほしいわけですが
現状は消えません。
場当たり的な解決法ではなく、
もう少し根本的な解決策をご教授願いたく。

よろしくお願いいたします。
投稿者 刈谷勇  (社会人) 投稿日時 2009/9/9 19:27:31
こんにちは、kmkmさん。

先にことわっておきますが、ドラッグ&ドロップについては詳しくないのでほかにベストな方法や間違った回答をしているかも知れません。

DoDragDropの戻り値を判断して、ドラッグ&ドロップの対象となった文字列を削除してあげればいいのではないかと思います。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/9/9 20:45:37
ドロップされる側は、ドラッグ元がどのようにデータを管理しているかを知りません。
ドラッグする側は、ドロップ先がどのようにデータを表示しているのかを知りません。

なので、ドラッグ & ドロップの処理では、ドラッグ元ドロップ先のそれぞれが、
自身のデータのみを扱うべきであり、相手側を直接操作してはいけません。

刈谷勇さんが書かれているように、DoDragDrop の戻り値で判定してください。


Public Class Form1
    Private Sub Form1_Load(ByVal sender As ObjectByVal e As EventArgs) Handles MyBase.Load
        TextBox1.Text = "これはドラッグ元のテキストです。右ボタンでドラッグできます(Shiftで移動)。"
        TextBox1.Select(3, 5)
        TextBox1.AllowDrop = False
        TextBox2.Text = ""
        TextBox2.AllowDrop = True
    End Sub

    Private Sub TextBox2_DragOver(ByVal sender As Object, _
      ByVal e As DragEventArgs) Handles TextBox2.DragOver
        If e.Data.GetDataPresent(GetType(String)) Then
            'Shift キーが押されていれば移動扱い/押されていなければコピー 
            Dim IsShift As Boolean = CBool(ModifierKeys And Keys.Shift)
            If IsShift AndAlso CBool(e.AllowedEffect And DragDropEffects.Copy) Then
                e.Effect = DragDropEffects.Move
            ElseIf CBool(e.AllowedEffect And DragDropEffects.Copy) Then
                e.Effect = DragDropEffects.Copy
            End If
        End If
    End Sub

    Private Sub TextBox2_DragDrop(ByVal sender As Object, _
      ByVal e As DragEventArgs) Handles TextBox2.DragDrop
        TextBox2.Text = e.Data.GetData(GetType(String))
    End Sub

    Private downPos As Point = Point.Empty
    Private Sub TextBox1_MouseDown(ByVal sender As Object, _
      ByVal e As MouseEventArgs) Handles TextBox1.MouseDown
        'テキストの範囲選択と区別するために、 
        'ドラッグ & ドロップをマウス 右ボタンに割り当てる。 
        If CBool(e.Button And System.Windows.Forms.MouseButtons.Right) Then
            'ドラッグの開始位置 
            downPos = e.Location
        End If
    End Sub

    Private Sub TextBox1_MouseMove(ByVal sender As Object, _
      ByVal e As MouseEventArgs) Handles TextBox1.MouseMove
        If downPos.IsEmpty Then
            'ドラッグが開始されていない 
            Return
        End If

        'マウスの移動量がドラッグ開始量を越えているかを判定 
        Dim dragSize As Size = SystemInformation.DragSize
        Dim moveRect As New Rectangle( _
            downPos.X - dragSize.Width \ 2, _
            downPos.Y - dragSize.Height \ 2, _
            dragSize.Width, _
            dragSize.Height)
        If moveRect.Contains(e.Location) Then
            Return
        End If
        downPos = Point.Empty

        'コピーと移動を許可する 
        Dim effect As DragDropEffects
        effect = DragDropEffects.Copy Or DragDropEffects.Move

        '選択されたテキスト範囲をドラッグ 
        effect = TextBox1.DoDragDrop(TextBox1.SelectedText, effect)

        If CBool(effect And DragDropEffects.Move) Then
            '移動されたので選択範囲をクリアする 
            TextBox1.SelectedText = ""
        End If
    End Sub
End Class
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/9/9 21:05:02
なお、TextBox2 のテキスト全体を書き換えるのではなく、
ドロップさせた位置にドロップしたテキスト挿入させたいのであれば
TextBox2 の部分を以下のように書き換えてみてください。


Private Sub TextBox2_DragOver(ByVal sender As ObjectByVal e As DragEventArgs) Handles TextBox2.DragOver
    If e.Data.GetDataPresent(GetType(String)) Then
        'Shift キーが押されていれば移動扱い/押されていなければコピー 
        Dim IsShift As Boolean = CBool(ModifierKeys And Keys.Shift)
        If IsShift AndAlso CBool(e.AllowedEffect And DragDropEffects.Copy) Then
            e.Effect = DragDropEffects.Move
        ElseIf CBool(e.AllowedEffect And DragDropEffects.Copy) Then
            e.Effect = DragDropEffects.Copy
        End If
        Dim mousePos As Point = TextBox2.PointToClient(MousePosition)
        Dim caretPos As Integer = TextBox2.GetCharIndexFromPosition(mousePos)
        TextBox2.SelectionStart = caretPos
        TextBox2.Focus()
    End If
End Sub

Private Sub TextBox2_DragDrop(ByVal sender As ObjectByVal e As DragEventArgs) Handles TextBox2.DragDrop
    TextBox2.SelectedText = e.Data.GetData(GetType(String))
End Sub
投稿者 kmkm  (社会人) 投稿日時 2009/9/10 03:44:06
刈谷勇さん、魔界の仮面弁士さん。
ありがとうございます。

おかげさまで、Moveそのものにつきましては、
お二方がおっしゃられているDoDragDrapの戻り値を
見ることでうまく処理することができました。

ただ残念ながら一点問題が残っており、
もし解決法がお分かりでしたら、ご助言ください。

問題といいますのは、Moveだけでなく、Copyもサポートした後で、
他のアプリ(たとえばワードパッド)にMoveしようといたしますと、
勝手にCopyになってしまう点です。
・魔界の仮面弁士さんのコードでも、ちゃんとシフトを押して、
  マウスの右ボタンでドラッグしていっても、私の環境では、
 Copyになってしまいます。
・Textの選択状態などを考慮してしないで行ったもともとの
 私のコードに手を入れたものでも同様にだめでした。(左ボタン使用)
・どちらのコードにおいても、TextBox2への、MoveやCopyは
 それぞれちゃんと正常に動いております。
・ワードパッドにマウスカーソルを移動すると、
 「コピーマーク」(+がついたマーク)のカーソルに勝手になってしまいます。
・Moveしかサポートしていなかった私のコードを単に
  DoDragDropの戻り値を見るようにしただけのMoveのみを行うコードは
 さすがにコピーにならず、Moveのまんまでした。
 (マウスカーソルも+がつかず、また、TextBox1のTextが消えます)


よろしくお願いいたします。
投稿者 (削除されました)  () 投稿日時 2009/9/10 05:22:36
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/9/10 06:21:37
> 他のアプリ(たとえばワードパッド)にMoveしようといたしますと、
> 勝手にCopyになってしまう点です。

それはワードパッド側の仕様です。

他のアプリへのドロップ時の動作は、VB からは制御できません。
VB から指定できるのは、DoDragDrop の引数である データ(data As Object)と、
使用可能な動作(allowedEffects As DragDropEffects)のみです。


ワードパッドの場合、ドラッグ元とドロップ先が、それぞれ同一アプリ上の内であれば、
「そのままでは Move」「Ctrl 押下で Copy」という動作モードになるのですが、
ワードパッドを 2 つ起動して、その間で文字列をドラッグしようとした場合、
「そのままでは Copy」「Alt 押下で Move」という動作になっています。

そして、VB からドロップした場合というのは、後者の状態にあたりますから、
Alt を押さない限りは、Move 扱いにならないのです。

ただしこれは、DoDragDrop に渡す DragDropEffects を
'コピーと移動を許可する  
Dim effect As DragDropEffects
effect = DragDropEffects.Copy Or DragDropEffects.Move
にしていた場合の話です。

Move のみを指定した場合には、ワードパッドは「常に Move」として処理されますし、
Copy のみを指定した場合には、ワードパッドは「常に Copy」として処理されます。

これはドロップ先は、allowedEffects で許可されている動作モードのみが
利用可能であるからです。(allowedEffects を無視するアプリもありますが…)


# DragDropEffects には、他にも Scroll や Link など幾つかのモードがありますが、
# それらをドロップ先のアプリがどのように処理するかは、ドロップ先が決める事であって、
# ドラッグ元である VB 側は一切関与する必要がありませんし、関与すべきではありません。
投稿者 kmkm  (社会人) 投稿日時 2009/9/10 06:49:51
魔界の仮面弁士さん。
大変ご丁寧にありがとうございました。
おっしゃられていた内容すべてを確認いたしました。

*ちなみに秀丸エディタでも、エディタ内(CTRLでコピー)と、
 別のアプリから(SHIFTを押して初めてムーブになる)で
 動きが違うことを初めて確認いたしました。
 (もう長く使っているんですが知りませんでした。。。)

これにてひとまず一件落着とさせていただきます。
ありがとうございました。