user1137313
user1137313

Reputation: 2410

Delphi treeview drag and drop between nodes

I want to allow the user to arrange a treeView's nodes as he likes, but I am running into a lot of problems. Like for example:

That's not good. I want a behavior like in this picture

enter image description here

So the destination of the Node3, should be between Node1 and Node2. Of course the drop ON TOP of a node should be available, but this BETWEEN NODES functionality should be available too.

My code so far is:

procedure TForm3.FormCreate(Sender: TObject);
begin
  tv.FullExpand;
end;

procedure TForm3.tvDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  TargetNode, SourceNode : TTreeNode;
begin
  with TV do
  begin
    TargetNode := GetNodeAt(X,Y); // Get target node
    SourceNode := Selected;
    if (TargetNode = nil) then
    begin
      EndDrag(False);
      Exit;
    end;
    MoveNode(TargetNode,SourceNode);
    SourceNode.Free;
  end;
end;

procedure TForm3.tvDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if (Sender = TV) then // If TRUE than accept the draged item
  begin
    Accept := True;
  end;
end;
procedure TForm3.tvEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  TV.Repaint;
end;

procedure TForm3.tvMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  tv.BeginDrag(false,-1);
end;

Procedure TForm3.MoveNode(TargetNode, SourceNode : TTreeNode);
var
  nodeTmp : TTreeNode;
  i : Integer;
begin
  with TV do
  begin
    nodeTmp := Items.AddChild(TargetNode,SourceNode.Text);
    for i := 0 to SourceNode.Count -1 do
    begin
      MoveNode(nodeTmp,SourceNode.Item[i]);
    end;
  end;
end;

where of course TV is a TTreeView.

Upvotes: 4

Views: 12438

Answers (1)

David Heffernan
David Heffernan

Reputation: 612954

First of all, throw away all the code that you currently have. It's got too many flaws to make it worthwhile discussing.

The minimum code needed for this is as follows:

procedure TMyForm.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  Src, Dst: TTreeNode;
begin
  Src := TreeView1.Selected;
  Dst := TreeView1.GetNodeAt(X,Y);
  Src.MoveTo(Dst, naAdd);
end;

procedure TMyForm.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
  Src, Dst: TTreeNode;
begin
  Src := TreeView1.Selected;
  Dst := TreeView1.GetNodeAt(X,Y);
  Accept := Assigned(Dst) and (Src<>Dst);
end;

You need to set DragMode to dmAutomatic for the tree view. And you also need to hook up the two events to OnDragDrop and OnDragOver.

If you wish to implement different logic in the drop to handle adding as a sibling (before or after), or adding as a child, pass a different value for the second parameter of MoveTo.

The possibilities are listed here:

type
  TNodeAttachMode = (naAdd, naAddFirst, naAddChild, naAddChildFirst, naInsert);

These options are described in the documentation.

Upvotes: 14

Related Questions