Drag & drop
Flexible TreeView provides flexible drag & drop support within one treeview, between many treeviews, or between treeview and any other control.
The drag & drop operation consists from these parts (see the picture below):
(1)
- Nodes being dragged.(2)
- Node where to drop nodes.(3)
- Drop marker.(4)
- Drag & drop content thumbnail. It can be either a dragged nodes image or a custom image.
Drag & drop options#
All drag & drop settings are stored in the DragDropOptions treeview property and allow you to control drag & drop behavior:
- AllowDrag - defines whether the treeview can start drag nodes.
- AllowDrop - defines whether the treeview can drop a node.
- DragStartSensitivity - the size of a rectangle in pixels, centered on the point the mouse button was pressed, within which a drag operation will not begin.
- DropAction - defines an automatic action to do by the treeview when nodes have been dropped.
- ShowDragContent - indicates whether to show an image, near the mouse cursor, of the nodes being dragged.
- DragContentOpacity - defines the opacity level of the dragged content image near the mouse cursor. Works only when the ShowDragContent property is enabled.
DropAction#
Flexible TreeView allows to perform an automatic action when nodes dropped on drag & drop. To define such an action use DropAction property.
Available actions:
- Notify - do not do any automatic actions, instead notify about termination of drag & drop operation by the DragDrop event.
- AutoMove - automatically move dragged nodes into the specified node in this or any other tree.
- AutoCopy - copy nodes if, while dragging the nodes, the Control button was pressed. Use the Clone node method to copy the nodes.
- AutoMoveOrCopy - use the AutoMove mode if the Control button wasn't pressed when you dragged the nodes, or use the AutoCopy mode in case the Control button was pressed.
Info
If you created an inheritor of the Node class (or its inheritor) and use AutoCopy mode, you have to override the Clone node method and there clone all node properties you added.
AutoCopy and NodeControlContainer#
If you use NodeControlContainer node control to display a custom control in the node when the AutoCopy mode is enabled, the displayed custom control should implement the ICloneable interface to let the node be completely cloned.
Drag & drop run-time information#
All the information about current drag & drop operation is stored in the DragDropState treeview property.
You have these options to analyze the drag & drop in run-time:
- Position - defines where to drop nodes being dragged in relation to the node in the TargetNode property. Available values are Inside, Before and After.
- TargetNode - a node where to drop nodes not taking into account the drop position (Before and After values) in the Position property.
- RealTargetNode - a node where to drop nodes taking into account the drop position in the Position property. If the Position is Before, this property returns a previous visible node to the TargetNode node; if After - next visible node.
- NodeControl - a node control under the mouse cursor.
- Effects - allowed drag & drop effects (move, copy and so on).
- IsDragging - defines whether current drag & drop operation is in action right now.
- ContentThumbnailImage - intended to change an image near the mouse cursor when drag & drop in action. If custom image is not defined, treeview shows nodes being dragged image.
Drag & drop content#
By default, Flexible TreeView put the selected nodes into an instance of DragDropContent type. It holds the dragging nodes in the Nodes field and treeview that initiated the dragging operation in the Treeview field.
To access the dragging content the data of ARMSoft.FlexibleTreeView.DragDropContent type need to be requested from the drag & drop event arguments:
Also, Flexible TreeView allows to override the dragging content by overriding the CreateDragDataObject treeview method and returning a DataObject instance with a custom dragging content, as shown below.
class MyCustomTreeview : FlexibleTreeView
{
protected override DataObject CreateDragDataObject(List<Node> nodes, DragDropEffects allowedEffects)
{
string focusedNode = FocusedNode.Text;
return new DataObject(focusedNode);
}
}
// Handling the above string content in the drop target:
string dropContent = (string)e.Data.GetData(typeof(string));
Private Class MyCustomTreeview
Inherits FlexibleTreeView
Protected Overrides Function CreateDragDataObject(nodes As List(Of Node), allowedEffects As DragDropEffects) As DataObject
Dim focusedNode As String = FocusedNode.Text
Return New DataObject(focusedNode)
End Function
End Class
' Handling the above string content in the drop target:
Private dropContent As String = DirectCast(e.Data.GetData(GetType(String)), String)
Monitor and tune the drag & drop operation#
By default, Flexible TreeView covers the most frequently used drag & drop cases with help of separate settings, accessible via the DragDropOptions treeview property.
In case the drag & drop operation behavior need to be tuned or changed, the DragOver treeview event should be handled as shown below.
tree.DragOver += OnDragOver;
void OnDragOver(object sender, DragEventArgs args)
{
FlexibleTreeView treeview = (FlexibleTreeView)sender;
DragDropContent dragContent = (DragDropContent)args.Data.GetData(typeof(DragDropContent));
Node node = dragContent.Nodes[0];
if (node.Text == "Some node" && treeview.DragDropState.Position == eDropPosition.Inside)
{
args.Effect = DragDropEffects.Move;
}
else
{
args.Effect = DragDropEffects.None;
}
}
tree.DragOver += OnDragOver;
Private Sub OnDragOver(sender As Object, args As DragEventArgs)
Dim treeview As FlexibleTreeView = DirectCast(sender, FlexibleTreeView)
Dim dragContent As DragDropContent = DirectCast(args.Data.GetData(GetType(DragDropContent)), DragDropContent)
Dim node As Node = dragContent.Nodes(0)
If node.Text = "Some node" AndAlso treeview.DragDropState.Position = eDropPosition.Inside Then
args.Effect = DragDropEffects.Move
Else
args.Effect = DragDropEffects.None
End If
End Sub
In the DragOver event handler above, the sender
parameter is a Flexible TreeView instance where the drag & drop operation is going on. The args
input parameter should be used to get the drag & drop content, i.e. the nodes being dragged. Based on some information the current drag & drop operation effect (allowed action) could be changed via args.Effect property, as shown above.
The DragDropState treeview property is used to get an additional information about the going drag & drop operation, like Position, TargetNode, NodeControl, etc.
Drag & drop within same treeview#
Flexible TreeView supports drag & drop within one treeview without a single line of code. To do that, enable the DragDropOptions.AllowDrag and DragDropOptions.AllowDrop treeview properties to allow you to drag and drop nodes respectively; and set the drop action using the DragDropOptions.DropAction treeview property.
Drag & drop between Flexible TreeViews#
Flexible TreeView allows you to drag & drop nodes between treeviews without a single line of code. To do that, enable the DragDropOptions.AllowDrag and DragDropOptions.AllowDrop treeview properties to allow you to drag and drop nodes respectively; and set the drop action using the DragDropOptions.DropAction treeview property.
Drag & drop between treeview and other controls#
Flexible TreeView allows to drag & drop nodes between the treeview and any other control. To do that, enable the DragDropOptions.AllowDrag, DragDropOptions.AllowDrop treeview properties; select appropriate drop action using the DragDropOptions.DropAction property and use the code below depends on the drag & drop direction.
Tip
The drag & drop operation is initiated by calling the DoDragDrop method for the source control.
Drag & drop from an other control into a treeview#
- Enable the drag & drop in the source control. For example, for standard ListBox control you need to handle the MouseDown event, gather the dragging content and call DoDragDrop method like this:
// 'listBox' below is a ListBox control that initiates the drag & drop.
void listBox_MouseDown(object sender, MouseEventArgs e)
{
int indexOfItem = listBox.IndexFromPoint(e.X, e.Y);
if (indexOfItem >= 0 && indexOfItem < listBox.Items.Count)
{
// select the clicked listbox item.
listBox.ClearSelected();
listBox.SelectedIndex = indexOfItem;
// start drag & drop.
listBox.DoDragDrop(listBox.Items[indexOfItem], DragDropEffects.Copy);
}
}
' 'listBox' below is a ListBox control that initiates the drag & drop.
Private Sub listBox_MouseDown(sender As Object, e As MouseEventArgs)
Dim indexOfItem As Integer = listBox.IndexFromPoint(e.X, e.Y)
If indexOfItem >= 0 AndAlso indexOfItem < listBox.Items.Count Then
' select the clicked listbox item.
listBox.ClearSelected()
listBox.SelectedIndex = indexOfItem
' start drag & drop.
listBox.DoDragDrop(listBox.Items(indexOfItem), DragDropEffects.Copy)
End If
End Sub
- Handle the DragEnter and DragDrop event in the target treeview:
void tree_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
void tree_DragDrop(object sender, DragEventArgs e)
{
FlexibleTreeView tree = (FlexibleTreeView)sender;
if(tree.DragDropState.RealTargetNode == null)
return;
// import data from an external control into treeview.
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string text = (string)e.Data.GetData(DataFormats.Text);
Node node = new Node(text);
node.AttachTo(tree.DragDropState.RealTargetNode);
}
}
Private Sub tree_DragEnter(sender As Object, e As DragEventArgs)
e.Effect = DragDropEffects.Copy
End Sub
Private Sub tree_DragDrop(sender As Object, e As DragEventArgs)
Dim tree As FlexibleTreeView = DirectCast(sender, FlexibleTreeView)
If tree.DragDropState.RealTargetNode Is Nothing Then
Return
End If
' import data from an external control into treeview.
If e.Data.GetDataPresent(DataFormats.StringFormat) Then
Dim text As String = DirectCast(e.Data.GetData(DataFormats.Text), String)
Dim node As New Node(text)
node.AttachTo(tree.DragDropState.RealTargetNode)
End If
End Sub
Note that above we drag a string content from the source listbox to the treeview. If you want to drag a complex content, you need to handle it properly in the target treeview.
Drag & drop from a treeview into an other control#
Source part (Flexible TreeView): Flexible TreeView does not require any additional code to support dragging of nodes except that the AllowDrag property must be enabled. With this property change the treeview handles the drag & drop operation start and sets the currently selected nodes as a drag & drop content.
Destination part (other control): The destination control need to handle the DragEnter event to adjust the allowed drag effect and the DragDrop event to proceed the dropped content, as shown below.
void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
e.Effect = DragDropEffects.All;
}
void OnDragDrop(object sender, DragEventArgs e)
{
DragDropContent dropContent = (DragDropContent)e.Data.GetData(typeof(DragDropContent));
IList<Node> nodes = dropContent.Nodes;
foreach(Node node in nodes)
{
// proceed the dropped nodes...
}
}
Private Sub OnDragEnter(sender As Object, e As System.Windows.Forms.DragEventArgs)
e.Effect = DragDropEffects.All
End Sub
Private Sub OnDragDrop(sender As Object, e As DragEventArgs)
Dim dropContent As DragDropContent = DirectCast(e.Data.GetData(GetType(DragDropContent)), DragDropContent)
Dim nodes As IList(Of Node) = dropContent.Nodes
For Each node As Node In nodes
' proceed the dropped nodes...
Next
End Sub
Drag & drop from a treeview into a WPF control#
WPF requires that the dragging content be serializable while the Node class (Flexible TreeView's dragging content by default) is not supposed to be so. To deal with this a custom serializable container that holds the dragging nodes need to be passed into the drag & drop infrastructure (DoDragDrop method). To do so a descendant treeview class need to be created and the CreateDragDataObject method is overridden, as shown below:
[Serializable]
class DragDropContainer
{
public string[] Items { get; set; }
}
class MyCustomTreeview : FlexibleTreeView
{
protected override DataObject CreateDragDataObject(List<Node> nodes, DragDropEffects allowedEffects)
{
List<string> nodeText = new List<string>();
foreach(Node node in nodes)
{
nodeText.Add(node.Text);
}
DragDropContainer container = new DragDropContainer();
container.Items = nodeText.ToArray();
return new DataObject(container);
}
}
[Serializable]
Class DragDropContainer
Public Property Items() As String()
Get
Return _items
End Get
Set
_items = Value
End Set
End Property
Private _items As String()
End Class
Class MyCustomTreeview
Inherits FlexibleTreeView
Protected Overrides Function CreateDragDataObject(nodes As List(Of Node), allowedEffects As DragDropEffects) As DataObject
Dim nodeText As New List(Of String)()
For Each node As Node In nodes
nodeText.Add(node.Text)
Next
Dim container As New DragDropContainer()
container.Items = nodeText.ToArray()
Return New DataObject(container)
End Function
End Class
Note
Notice that the DragDropContainer class is serializable above.
After that this container could be handled in a WPF control in its DragDrop event handler like this:
Auto-expanding#
When you drag nodes, Flexible TreeView can auto-expand a node under the mouse cursor to make it easy to drop dragged nodes in there. To enable this, set the DragDropOptions.AutoExpandMode treeview property to one of these values:
- Disabled - do not auto-expand a node under the mouse cursor.
- PlusMinus - auto-expand a node only if the mouse is over the plus-minus sign.
- PlusMinusAndNodeBody - auto-expand a node if the mouse is over it. You can also set a delay before auto-expanding by using the DragDropOptions.AutoExpandDelay treeview property.
Auto-collapsing#
If a node has been auto-expanded and the mouse exceeds beyond the node bounds, Flexible TreeView can auto-collapse this node. To enable that, enable the DragDropOptions.AutoCollapse treeview property (enabled by default). Also, you can set a delay before auto-collapse by using the DragDropOptions.AutoCollapseDelay treeview property.
Thumbnail image near the mouse cursor#
On drag & drop you can customize an image near the mouse cursor which shows the nodes that are being dragged. To do that, subscribe to the DragGetImage treeview event and assign the args.Image property to a custom image.
Additional API references#
Events#
- DragStarting - occurs before drag & drop started.
- DragGetImage - occurs when the treeview need a drag & drop image to show near the mouse cursor.
- FilterDragNodeControl - occurs when the treeview starts the drag & drop and need to show a thumbnail image near the mouse cursor with node controls of the nodes being dragged.