Skip to content

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

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:

DragDropContent dropContent = (DragDropContent)e.Data.GetData(typeof(DragDropContent));
IList<Node> nodes = dropContent.Nodes;

foreach(Node node in nodes)
{
    // proceed the dropped nodes...
}
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

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#

  1. 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
  1. 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:

protected override void OnDragEnter(DragEventArgs e)
{
    DragDropContainer container = (DragDropContainer)e.Data.GetData(typeof(DragDropContainer));
    string[] nodes = container.Items;

    foreach(string node in nodes)
    {
        // process the dropped nodes...
    }
}
Protected Overrides Sub OnDragEnter(e As DragEventArgs)
    Dim container As DragDropContainer = DirectCast(e.Data.GetData(GetType(DragDropContainer)), DragDropContainer)
    Dim nodes As String() = container.Items

    For Each node As String In nodes
        ' process the dropped nodes...
    Next
End Sub

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.