VB.NETでツリービュー上でドラッグアンドドロップできる処理を作成しているのですが。
1)ドラッグ中にエクスプローラのように、ツリーをスクロールをしたい。
2)ドラッグ中にエクスプローラのように、もし、ツリーにサブツリーがある場合には、展開をしたい。
3)ドラッグ中にエクスプローラのように、どこにドラッグされるか、
ハイライト表示(VB6.0のころは、DropHighlightメソッドでできた?)又は、
ドラッグツリー名の表示をしたい。
Win2000
VB.NET2003
VB.NETでツリービュー上でドラッグアンドドロップできる処理を作成しているのですが。
1)ドラッグ中にエクスプローラのように、ツリーをスクロールをしたい。
2)ドラッグ中にエクスプローラのように、もし、ツリーにサブツリーがある場合には、展開をしたい。
3)ドラッグ中にエクスプローラのように、どこにドラッグされるか、
ハイライト表示(VB6.0のころは、DropHighlightメソッドでできた?)又は、
ドラッグツリー名の表示をしたい。
> 1)...ツリーをスクロールをしたい。
TreeNodeのPrevVisibleNode, NextVisibleNodeに対してEnsureVisible
> 2)...ツリーにサブツリーがある場合には、展開をしたい。
TreeNodeのExpand
> 3)...どこにドラッグされるかハイライト表示
> ...又は、ドラッグツリー名の表示をしたい。
ドラッグ先をTreeView1.GetNodeAt()で取得してBackColor等を設定
ドラッグツリー名はTreeNodeのFullPathで取得
でいけると思いますが「エクスプローラのように」となると
delayタイムを設けないと快適な操作感は得られないので、
ちょっとゴチャゴチャしそうですね。この辺りの実装方法は
いろいろあるかと思いますが、一例としてこんな感じ。
(あまり検証していないので不具合ある可能性大(^^;)
Private m_nodeDrag As TreeNode = Nothing
Private m_nodeHighlight As TreeNode = Nothing
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
TreeView1.AllowDrop = True
'キリよくサイズ調整
TreeView1.Height = TreeView1.VisibleCount() * TreeView1.ItemHeight() + 4
End Sub
'Dragの開始
Private Sub TreeView1_ItemDrag(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ItemDragEventArgs _
) Handles TreeView1.ItemDrag
m_nodeHighlight = Nothing
m_nodeDrag = CType(e.Item, TreeNode)
m_nodeDrag.BackColor = Color.LightGray
TreeView1.SelectedNode = Nothing
TreeView1.DoDragDrop(m_nodeDrag, DragDropEffects.Move)
End Sub
'Dragさまよい中
Private Sub TreeView1_DragOver(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DragEventArgs _
) Handles TreeView1.DragOver
Static s_hoverStartTime As DateTime = DateTime.Now
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode") Then
Dim nodeHover As TreeNode = _
TreeView1.GetNodeAt(TreeView1.PointToClient(New Point(e.X, e.Y)))
Dim timeHover As Integer = _
CInt(DateTime.Now.Subtract(s_hoverStartTime).TotalMilliseconds)
If timeHover >= 800 Then
'delay800msで、自動展開
nodeHover.Expand()
s_hoverStartTime = DateTime.Now
ElseIf timeHover >= 300 Then
'delay300msで、自動Scroll
If Not nodeHover.PrevVisibleNode Is Nothing AndAlso Not nodeHover.PrevVisibleNode.IsVisible Then
nodeHover.PrevVisibleNode.EnsureVisible()
s_hoverStartTime = DateTime.Now
End If
If Not nodeHover.NextVisibleNode Is Nothing AndAlso Not nodeHover.NextVisibleNode.IsVisible Then
nodeHover.NextVisibleNode.EnsureVisible()
s_hoverStartTime = DateTime.Now
End If
End If
'Dropターゲットの着色とカーソル状態設定
If nodeHover Is m_nodeHighlight Then
e.Effect = DragDropEffects.Move
Me.Text = nodeHover.FullPath
Else
'(!)Drag元の配下へDropできないようにするとかは、このSampleでは考慮していない
If Not m_nodeHighlight Is Nothing Then
m_nodeHighlight.BackColor = TreeView1.BackColor
m_nodeHighlight.ForeColor = TreeView1.ForeColor
End If
If nodeHover Is m_nodeDrag Then
e.Effect = DragDropEffects.None
Else
nodeHover.BackColor = SystemColors.Highlight
nodeHover.ForeColor = SystemColors.HighlightText
m_nodeHighlight = nodeHover
s_hoverStartTime = DateTime.Now
e.Effect = DragDropEffects.Move
End If
End If
End If
End Sub
'Drop発生
Private Sub TreeView1_DragDrop(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DragEventArgs _
) Handles TreeView1.DragDrop
'TODO:Dropの処理を記述
End Sub
'D&Dの完了or中断
Private Sub TreeView1_MouseUp(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs _
) Handles TreeView1.MouseUp
If Not m_nodeHighlight Is Nothing Then
m_nodeHighlight.BackColor = TreeView1.BackColor
m_nodeHighlight.ForeColor = TreeView1.ForeColor
m_nodeHighlight = Nothing
End If
If Not m_nodeDrag Is Nothing Then
m_nodeDrag.BackColor = TreeView1.BackColor
m_nodeDrag = Nothing
End If
End Sub
<参考URL>
▼Windows フォームにおけるドラッグ アンド ドロップ操作の実行
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vbcon/html/vbtskstartingdragoperations.asp
▼宇宙仮面のC# プログラミングへようこそ! ドラッグアンドドロップ
http://ukamen.hp.infoseek.co.jp/Programming1/DragAndDrop/
ていねいな説明たいへんありがとうございました。
とてもたすかりました。
ここに、フォルダを移動する処理を書いているのですが、
フォルダの移動処理に失敗した場合、(Drag元と同じ場所へDropしたようなときに)、表示ツリービューの先頭が選択されてしまいます。
なぜでしょうか?
'Drop発生
Private Sub TreeView1_DragDrop(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DragEventArgs _
) Handles TreeView1.DragDrop
'TODO:Dropの処理を記述
End Sub
さて、教えていただいたコードに、
リストビューから、ツリービューへドラッグする処理を追加したいと思い、
(エクスプローラのように)
以下のように追加してみました。
Private Sub ListView1_ItemDrag(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemDragEventArgs) Handles ListView1.ItemDrag
If e.Button = MouseButtons.Left Then
'invoke the drag and drop operation
m_nodeHighlight = Nothing
DoDragDrop(e.Item, DragDropEffects.Move Or DragDropEffects.Copy)
End If
End Sub
1回目にリストービューからツリービューへドラッグすると、
マウスカーソルが丸に×の表示で、少しツリービュー上でドラッグしていると、ドロップできるようになります。なぜでしょうか?
その後は、すぐドロップできます。
プログラム起動直後に、
すぐにドロップできるようにするには、どうしたらよいのでしょうか?
> ここに、フォルダを移動する処理を書いているのですが、
まず最初に断っておきますと、あのSampleはご質問の
(1),(2),(3)を満たすだけの例であり、TreeViewから
TreeViewへのDragAndDropしか想定していません。
よって、DragOverが走る前に、まずTreeView1_ItemDrag
が走っているはずだという前提があります。
ListViewからも持ってくるとなると、
.GetDataPresent("System.Windows.Forms.ListViewItem")
とするだけではなく、他にも色々と考えるべき点が
出てくることになります。
継ぎ足しで組んでいると、結果的にトータルで生産性の
悪い事態になるでしょうから、SampleはあくまでSample
として、ご自身で各Methodを理解しながら0から組んでみ
ることをおすすめします。
> ...ツリービューの先頭が選択されてしまいます。
> なぜでしょうか?
TreeView1_ItemDrag イベント内の
TreeView1.SelectedNode = Nothing
は不要でした。Drag元はまずSelectedになると私は勘違い
していたので(=HighlightColorと区別がつかない)、この
1行を入れましたが、そうとは限らないので意味のない1行
です。選択がリセットされるなら、これが怪しいでしょう。
> 1回目にリストービューからツリービューへドラッグすると、
> マウスカーソルが丸に×の表示で、少しツリービュー上でド
> ラッグしていると、ドロップできるようになります。なぜでしょうか?
これは再現しなかったのですが??? (on VS2002(fw1.0-SP2))
「マウスカーソルが丸に×」になるということは、
e.Effect = DragDropEffects.None
に入っているか、あるいはこの辺りのCodeに全く到達でき
ていないか、TreeView1_DragOverイベント自体呼ばれてないか
でしょうから、その辺りでConsole.WriteLine()入れるなり
して調べてみてください。
デバッグしてみたのですが、いまいちわからないので、
もう少し教えてください。すみません。
>は不要でした。Drag元はまずSelectedになると私は勘違い
>していたので(=HighlightColorと区別がつかない)、この
>1行を入れましたが、そうとは限らないので意味のない1行
>です。選択がリセットされるなら、これが怪しいでしょう。
そのとおりでした。
> 1回目にリストービューからツリービューへドラッグすると、
> マウスカーソルが丸に×の表示で、少しツリービュー上でド
> ラッグしていると、ドロップできるようになります。なぜでしょうか?
> これは再現しなかったのですが??? (on VS2002(fw1.0-SP2))
「マウスカーソルが丸に×」になるということは、
丸に斜め線でした。
これについては、以下のコードを追加することにより、一応解消されました。
Private Sub ListView1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListView1.DragEnter
' Check to be sure that the drag content is the correct type for this
' control. If not, reject the drop.
'If (e.Data.GetDataPresent("System.Windows.Forms.listviewitem")) Then
' If the Ctrl key was pressed during the drag operation then perform
' a Copy. If not, perform a Move.
If (e.KeyState And CtrlMask) = CtrlMask Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
'Else
' e.Effect = DragDropEffects.None
'End If
End Sub
>よって、DragOverが走る前に、まずTreeView1_ItemDrag
>が走っているはずだという前提があります。
>ListViewからも持ってくるとなると、
>.GetDataPresent("System.Windows.Forms.ListViewItem")
>とするだけではなく、他にも色々と考えるべき点が
>出てくることになります。
このへんの説明がだいぶひっかかるのですが、
TreeView上でのドラッグは、正常にできるということは、
ListviewからTreeviewへドラッグすると、
TreeView1_DragOverイベントは、起きるみたいなのですが、数秒後のタイムラグのあと動き始めるみたいです。当初は、nodeHover=nothingなのですが、数秒後たった後、いきなり表示され始める。
一度でも動き出すと次からは、正常に動いているみたいです。
どうしてよいか今のままですとわからない状態です。
やはり、TreeView1_ItemDragを走らせる何かをしないとだめなのでしょうか?
ここからの文章がへんです。
このへんの説明がだいぶひっかかるのですが、
ListviewからTreeviewへドラッグすると、
TreeView1_DragOverイベントは、起きるみたいなのですが、数秒後のタイムラグのあと動き始めるみたいです。(ツリーのハイライト表示が開始されます。)
当初は、nodeHover=nothingなのですが、数秒後たった後、いきなり表示され始める。
一度でも動き出すと次からは、ListviewからTreeviewへドラッグすると、すぐにハイライト表示されます。
どうしてよいか今のままですとわからない状態です。
やはり、TreeView1_ItemDragを走らせる何かをしないとだめなのでしょうか?
Private Sub TreeView1_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles TreeView1.DragOver
Dim nodeHover As TreeNode = _
TreeView1.GetNodeAt(TreeView1.PointToClient(New Point(e.X, e.Y)))
のあとに、
If nodeHover Is Nothing Then
nodeHover = TreeView1.SelectedNode
End If
を追加したら、うまくいきました。
ありがとうございました。
> 一度でも動き出すと次からは
という辺りは、おそらく TreeView1_MouseUp に原因があります。
_MouseUpが走るのは、
「マウスボタンがこのControlの上でUpされたよ」ではなくて、
「以前このControlでDownされたマウスボタンが(今やっと)Upされたよ」
です。
DragDropを行った場合、その結果は完了させたか、中断したかの
2つに1つです。Sampleではメンバー変数でNodeを保持している
ので、これを開放する後始末をしたいがため、TreeView1_MouseUp
を入れましたが、本来
成功の場合=DragDropで行う。
中断の場合=MouseUpか或いは他に適切なところがあればそこで。
とすべきですが、SampleゆえMouseUpでまとめて行っています。
ListViewからDragAndDropするなら、TreeView1_MouseUpイベント
は何の働きもしていません。これがメンバー変数の状態に影響を
与えているのです。
すでに解決とのことですが、
基本的に、メンバー変数を使わずにイベント引数から取得するよ
うにすることは望ましい方向だと思います。
ただ、どちらのControlでどのようなイベントが起こるのかには
十分な調査をすることを忘れずに!
ツイート | ![]() |