7

I've got a list of nodes. I would like to add a drag-and-drop-to-rearrange feature, but I don't know how to go about doing this.

I tried using TVirtualStringTree's OnDragDrop event, but I couldn't figure it out. I looked at the documentation and there is sadly no minimal sample code there for plain node drag-dropping.

Please note, that it's just a single-level list. No hierachy. :)

Jeff
  • 12,085
  • 12
  • 82
  • 152
  • It depends on how you've built your tree. Provide more information about that. – Linas Dec 13 '11 at 10:32
  • Everything is stored in the PVirtualNode's Data, if thats what you mean? It's like a listview actually. – Jeff Dec 13 '11 at 10:40

2 Answers2

16

If you're getting data through GetNodeData than your drag and drop could be implemented like this:

uses
  ActiveX;

Assign drag events to the tree:

OnDragAllowed:

procedure TForm1.vt1DragAllowed(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  var Allowed: Boolean);
begin
  Allowed := True;
end;

OnDragOver:

procedure TForm1.vt1DragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState;
  State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
begin
  Accept := (Source = Sender);
end;

OnDragDrop:

procedure TForm1.vt1DragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject;
  Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
var
  pSource, pTarget: PVirtualNode;
  attMode: TVTNodeAttachMode;
begin
  pSource := TVirtualStringTree(Source).FocusedNode;
  pTarget := Sender.DropTargetNode;

  case Mode of
    dmNowhere: attMode := amNoWhere;
    dmAbove: attMode := amInsertBefore;
    dmOnNode, dmBelow: attMode := amInsertAfter;
  end;

  Sender.MoveTo(pSource, pTarget, attMode, False);

end;

Also do not forget to set toAutoDeleteMoveNodes to False in TreeOptions.AutoOptions.

Linas
  • 5,485
  • 1
  • 25
  • 35
  • Any ideas on how to move multiple nodes (in a single-level list) ? I tried getting all the selected nodes - not only the `FocusedNode` - but with little success. Although the SelectedCount is greater than 1 and the `GetFirstSelected` returns the `FocusedNode`, 'GetNextSelected` returns nil. – DarkByte Jul 06 '12 at 06:18
  • @Linas - how would you get this to work in conjunction with your svTrees code? When using this with the solution you posted under "Tree-like Datastructure (for use with VirtualTreeview)" it appears to only be updating the VSTree and not the underlying data structure – TheSteven Nov 02 '13 at 19:55
  • 1
    Additionally I found the following properties relevant to enabling the drag drop - TreeOptions > MiscOptions > `toAcceptOLEDrop=True` and TreeOptions > MiscOptions > `toFullRowDrag=True` as well as setting `DragType=dtOLE` – user30478 Jul 07 '21 at 10:50
2

Multiple nodes drag'n'drop:

procedure TForm1.vst(Sender: TBaseVirtualTree;
  Source: TObject; DataObject: IDataObject; Formats: TFormatArray;
  Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
var
  pSource, pTarget: PVirtualNode;
  attMode: TVTNodeAttachMode;
  List: TList<PVirtualNode>;
begin
  pTarget := Sender.DropTargetNode;

  case Sender.GetNodeLevel(pTarget) of
    0:
      case Mode of
        dmNowhere:
          attMode := amNoWhere;
        else
          attMode :=  amAddChildLast;
      end;
    1:
      case Mode of
        dmNowhere:
          attMode := amNoWhere;
        dmAbove:
          attMode := amInsertBefore;
        dmOnNode, dmBelow:
          attMode := amInsertAfter;
      end;

  end;
  List:= TList<PVirtualNode>.create();
  pSource :=  Sender.GetFirstSelected();
  while Assigned(pSource) do
  begin
     List.Add(pSource);
     pSource := Sender.GetNextSelected(pSource);
  end;

  for pSource in List do
   Sender.MoveTo(pSource, pTarget, attMode, False);

 List.Free;
end;