Implementing Drag-And-Drop Functionality
- 6 minutes to read
Drag-and-drop allows end-users to drag one control (or its items) to another within the same application or rearrange items within the same control. There are four different situations when you may need to implement the drag-and-drop feature within the TreeList control:
Rearranging nodes within the TreeList control by changing their parent nodes.
Drag and drop from a VCL control onto a TreeList control.
Drag and drop from a TreeList control onto a VCL control.
Drag and drop between two TreeList controls.
In this topic we will discuss all four types of drag-and-drop.
Drag and drop within a TreeList control
This is the easiest case, because it can be implemented automatically by setting the DragMode property of the TreeList control to dmAutomatic in the Object Inspector:
Additionally, at the very least you will have to provide an empty implementation of the OnDragOver event to enable drag-and-drop within the TreeList control.
If you are working with a data-aware TreeList control, it changes parent field values during drag-and-drop operations automatically. Set the OptionsData.AutoCalcKeyValue property to True to enable automatic changing of the node’s key field values.
On starting an application, you will be able to drag-and-drop nodes within the TreeList control.
Drag and drop from a VCL control onto a TreeList control
Let’s create a small application with one form containing two controls: TreeList and Listbox. An end-user will be able to drag items from the Listbox onto the TreeList.
Drop TcxTreeList and TListBox controls on a form.
Create one column in the TreeList control and set the OptionsView.ColumnAutoWidth property to True.
Add 5 items to the ListBox control.
Change ListBox properties: DragMode to dmAutomatic and MultiSelect to True.
- Write the following code for the OnDragOver event of the TreeList control to show that the TreeList can accept items from the ListBox control.
procedure TForm1.cxTreeList1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := Source = ListBox1;
end;
- Write the following code for the TreeList control’s OnDragDrop event to process the final part of the drag-and-drop operation.
procedure TForm1.cxTreeList1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
AParentNode, ANode: TcxTreeListNode;
I: Integer;
begin
if Source <> ListBox1 then exit;
// If the cursor was under a particular node, add items as children to that node.
AParentNode := cxTreeList1.HitTest.HitNode;
for I := 0 to ListBox1.Items.Count - 1 do
if ListBox1.Selected[I] then
begin
// add a new node
ANode := cxTreeList1.AddChild(AParentNode);
// set node value
ANode.Values[0] := ListBox1.Items[I];
end;
end;
The following image shows the application while dragging and after dropping:
As you can see, the dragged list box items (Item3 and Item4) are added to the TreeList control as Item1 children.
Drag and drop from a TreeList control onto a VCL control
Let’s use the TreeList and Listbox controls again.
Drop TcxTreeList and TListBox controls on a form.
Create one column in the TreeList control and set the OptionsView.ColumnAutoWidth property to True.
Invoke the TreeList control’s Items Editor and add several nodes.
Disable the TreeList control’s OptionsBehavior.ImmediateEditor and OptionsBehavior.AlwaysShowEditor properties and set DragMode to dmAutomatic.
Write the following code for the Listbox control’s OnDragOver and OnDragDrop events.
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := Source = cxTreeList1; // accept drag-and-drop for the TreeList control only
end;
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
I: Integer;
begin
// exit if the drag-and-drop operation was initialized from a control different from cxTreeList1
if Source <> cxTreeList1 then exit;
// loop through selected nodes and add them to the ListBox1 control
for I := 0 to cxTreeList1.SelectionCount - 1 do
ListBox1.Items.Add(cxTreeList1.Selections[I].Values[0]);
end;
The following image shows the application while dragging:
And the following image shows the TreeList control after dropping:
Drag and drop between two TreeList controls
Dragging one TreeList control to another has one major difference from the examples above: the tree structure must be recreated on the target TreeList control.
Drop a TcxTreeList control on a form.
Create a column in the TreeList control and set the OptionsView.ColumnAutoWidth property to True.
Invoke the Items Editor and add several nodes.
Disable the OptionsBehavior.ImmediateEditor and OptionsBehavior.AlwaysShowEditor properties and set DragMode to dmAutomatic.
Copy the TreeList control to the clipboard and paste it into the same form, so that we have two controls with the same settings.
- Write the following code for the OnDragOver and OnDragDrop events of the TreeList controls. Note that we use the same code for both controls.
procedure TForm1.cxTreeList1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
// accept drag-and-drop from the other TreeList control only
Accept := (Source is TcxTreeList) and (Sender <> Source);
end;
// Since we want to store the tree structure in the TreeList control
// the method looks pretty complex
procedure TForm1.cxTreeList1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
ASourceTreeList, ADestTreeList: TcxTreeList;
AParentNode: TcxTreeListNode;
// get the correct parent node
// new inserted nodes have their data property set to the source node
// for the original nodes, the data property equals nil
function GetParentNode(ANewNode: TcxTreeListNode): TcxTreeListNode;
var
I: Integer;
ANode: TcxTreeListNode;
begin
Result := nil;
// loop through all nodes in the destination TreeList control
for I := 0 to ADestTreeList.Nodes.Count - 1 do
// is it a new inserted node?
if ADestTreeList.Nodes.Items[I].Data <> nil then
begin
// get the source node
ANode := TcxTreeListNode(ADestTreeList.Nodes.Items[I].Data);
// if the new source node has ANode as parent
if ANewNode.HasAsParent(ANode) and
((Result = nil) or TcxTreeListNode(Result.Data).HasAsParent(ANode)) then
Result := ADestTreeList.Nodes.Items[I];
end;
if Result = nil then
Result := AParentNode;
end;
var
ANode, ASelectedNode: TcxTreeListNode;
I: Integer;
begin
if not (Source is TcxTreeList) then exit;
ASourceTreeList := TcxTreeList(Source);
ADestTreeList := TcxTreeList(Sender);
AParentNode := ADestTreeList.HitTest.HitNode;
// loop through all selected nodes in the source TreeList control
for I := 0 to ASourceTreeList.SelectionCount - 1 do
begin
ASelectedNode := ASourceTreeList.Selections[I];
// add a new node
ANode := ADestTreeList.AddChild(GetParentNode(ASelectedNode), ASelectedNode);
// set the data in cell(s)
ANode.Values[0] := ASelectedNode.Values[0]
end;
// set the data property for all nodes to nil
for I := 0 to ADestTreeList.Nodes.Count - 1 do
ADestTreeList.Nodes.Items[I].Data := nil;
end;
The following image shows the resulting application while dragging:
And the following image shows the drag-and-drop result:
Note
You may also find the following TreeList control properties useful when handling drag-and-drop operations: OptionsView.DropNodeIndicator, OptionsBehavior.AutoDragCopy, OptionsBehavior.DragCollapse, and OptionsBehavior.DragExpand.