0

How to copy all columns between two VirtualTreeView? In my case only the first one is copied.

image here

I use VirtualTreeView v6.6.

The code that I'm using:

type
  PItemNode1 = ^TItemNode1;

  TItemNode1 = record
    Name: WideString;
    Order: string;
    Quantity:String;
  end;

type
  PItemNode2 = ^TItemNode2;

  TItemNode2 = record
    Name: WideString;
    Order: string;
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Node: PVirtualNode;
begin
  VT.NodeDataSize := SizeOf(TItemNode1);
  VT2.NodeDataSize := SizeOf(TItemNode2);
  VT.RootNodeCount := 2;
  VT2.RootNodeCount := 10;
end;

Procedure for inserting OLE data

procedure TForm1.InsertData(Sender: TVirtualStringTree; DataObject: IDataObject;
  Formats: TFormatArray; Effect: Integer; Mode: TVTNodeAttachMode);
var
  FormatAccepted: Boolean; 
  i: Integer;
begin

  FormatAccepted := false;
  for i := 0 to High(Formats) do
  begin
    if Formats[i] = CF_VIRTUALTREE then
    begin
      if not FormatAccepted then
      begin
        Sender.ProcessDrop(DataObject, Sender.DropTargetNode, Effect, Mode);
        FormatAccepted := True;
      end;
    end;
  end;
end;

Drag and Drop precedures

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

procedure TForm1.VT2DragDrop(Sender: TBaseVirtualTree; Source: TObject;
  DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState;
  Pt: TPoint; var Effect: Integer; Mode: TDropMode);
  procedure DetermineEffect;
  begin
    if Shift = [] then
    begin
      if Source = Sender then
        Effect := DROPEFFECT_MOVE
      else
        Effect := DROPEFFECT_COPY;
    end
    else
    begin
      if (Shift = [ssAlt]) or (Shift = [ssCtrl, ssAlt]) then
        Effect := DROPEFFECT_LINK
      else if Shift = [ssCtrl] then
        Effect := DROPEFFECT_COPY
      else
    end;
  end;

var
  Attachmode: TVTNodeAttachMode;
  Nodes: TNodeArray;
  i: Integer;
begin
  Nodes := nil;
  case Mode of
    dmAbove:
      Attachmode := amInsertBefore;
    dmOnNode:
      Attachmode := amAddChildLast;
    dmBelow:
      Attachmode := amInsertAfter;
  else
    Attachmode := amNowhere;
  end;
  if DataObject = nil then
  begin
  //VCL
    if Source is TVirtualStringTree then
    begin
      DetermineEffect;

      Nodes := VT2.GetSortedSelection(True);

      if Effect = DROPEFFECT_COPY then
      begin
        for i := 0 to High(Nodes) do
          VT2.CopyTo(Nodes[i], Sender.DropTargetNode, Attachmode, false);
      end
      else
        for i := 0 to High(Nodes) do
          VT2.MoveTo(Nodes[i], Sender.DropTargetNode, Attachmode, false);
    end;
  end
  else
  begin
    // OLE drag&drop.
    if Source is TBaseVirtualTree then
      DetermineEffect
    else
    begin
      if Boolean(Effect and DROPEFFECT_COPY) then
        Effect := DROPEFFECT_COPY
      else
        Effect := DROPEFFECT_MOVE;
    end;
    InsertData(Sender as TVirtualStringTree, DataObject, Formats, Effect,
      Attachmode);
  end;
end;

procedure TForm1.VT2DragOver(Sender: TBaseVirtualTree; Source: TObject;
  Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode;
  var Effect: Integer; var Accept: Boolean);
// Return True, if AParent - child node of ANode.
  function IsNodeParent(AParent, ANode: PVirtualNode): Boolean;
  var
    NextParent: PVirtualNode;
  begin
    NextParent := AParent;
    repeat
      NextParent := NextParent.Parent;
    until (NextParent = Sender.RootNode) or (NextParent = nil) or
      (NextParent = ANode);
    Result := ANode = NextParent;
  end;

var
  i: Integer;
  Nodes: TNodeArray;
begin
  Accept := True;
  if (Assigned(Sender.DropTargetNode)) and
    (Sender.DropTargetNode <> Sender.RootNode) then
    Nodes := (Sender as TVirtualStringTree).GetSortedSelection(True);
  if Length(Nodes) > 0 then
  begin
    for i := 0 to Length(Nodes) - 1 do
    begin
      Accept :=
        (not IsNodeParent(Sender.DropTargetNode, Nodes[i]))
        and (not(Sender.DropTargetNode = Nodes[i]));
      if not Accept then Exit;
    end;
  end;
end;

Initialize the nodes of VT2(right VT in the picture above )and get the text for them

procedure TForm1.VT2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  ItemNode: PItemNode2;

begin
  ItemNode := Sender.GetNodeData(Node);
  if Assigned(ItemNode) then
  begin
    case Column of
      0:
        CellText := ItemNode^.Name;
      1:
        CellText := ItemNode^.Order;
    end;
  end;
end;

procedure TForm1.VT2InitNode(Sender: TBaseVirtualTree;
  ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  ItemNode: PItemNode2;
begin
  ItemNode := Sender.GetNodeData(Node);
  if Assigned(ItemNode) then
    if Length(ItemNode^.Name) = 0 then
      ItemNode^.Name := 'Node Index № ' + IntToStr(Node.Index);
      ItemNode^.Order := IntToStr(Node.Index);
end;

Get a new text after the node is dropped

procedure TForm1.VT2NewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; NewText: string);
var
  ItemNode: PItemNode2;
begin
  ItemNode := Sender.GetNodeData(Node);
  if Assigned(ItemNode) then
  begin
    case Column of
      0:
        ItemNode^.Name := NewText;
      1:
        ItemNode^.Order := NewText; // I've checked this line in the debugger. It is skipped
    end;
  end;
end;

Initialize the nodes of VT(left VT in the picture above )and get the text for them

procedure TForm1.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  ItemNode: PItemNode1;
begin
  ItemNode := Sender.GetNodeData(Node);
  if Assigned(ItemNode) then
    case Column of
      0:
        CellText := ItemNode^.Name;
      1:
          CellText := ItemNode^.Order;
    end;
end;
procedure TForm1.VTInitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  ItemNode: PItemNode1;
begin
  ItemNode := Sender.GetNodeData(Node);
  if Assigned(ItemNode) then
    if Length(ItemNode^.Name) = 0 then
      ItemNode^.Name := 'VT1_Node № ' + IntToStr(Node.Index);
      ItemNode^.Order := IntToStr(Node.Index);
end;
end.
Deluxe
  • 17
  • 2
  • 2
    Whats your trying code? – Ali Jun 02 '17 at 10:20
  • 2
    Please show the code you have sofar! Do you want the copies of the columns to show the same (physical) data in both vtv's or do you also want to make a copy of the data? – Tom Brunberg Jun 02 '17 at 10:28
  • @Tom, it will always need some data to be copied in the target node. Either copy of the source node data, or the reference to the source node (to query the source tree). In any case I would suggest having a separate collection for data and in nodes store only pointers to the items of that collection (not saving data by the node). – Victoria Jun 02 '17 at 10:56
  • @Victoria Probably we speak about the same thing with just different vocabulary. When I asked about showing same (physical) data I mean both vtv's **refer** to the **same data**, but I don't consider the references actual data. When I ask about making a copy of the data I mean exactly that, the two vtv's have **separate data** – Tom Brunberg Jun 02 '17 at 11:28
  • @Tom, now I see your point. I would definitely prefer separate collection(s) for data and with the node hold only a pointer to the collection item (using the tree as a view). If the data remains same in both trees, just one collection is needed (both trees can then display data from one collection which, when is modified outside the tree can very simply refresh the view in both trees by invalidating). If one of the trees can be separately modified, two separate collections will be needed (so as data records must be copied in case the tree node is a data holder). – Victoria Jun 02 '17 at 11:51
  • Thank you for your attention. I added my trying code. – Deluxe Jun 02 '17 at 12:37
  • _"Get a new text after the node is dropped"_ with the `OnNewText` event handler where you're overwriting node data does not seem to be correct. When dropping the node you should copy the data and the tree should then ask for _cell_ captions by the `OnGetText` (or `OnGetCellText`) event. – Victoria Jun 02 '17 at 12:57
  • @Victoria,could you give a small example? In my case only right tree can be modified, left tree acts as a source, Also in the future _left tree_ can contain the data that is not in the _right tree_. For example, `TItemNode1` record contains `quantity` field which is not in the `TItemNode2` record. – Deluxe Jun 02 '17 at 18:55
  • Sorry, I don't have Delphi by hand these days. My point was about about using a `TList` (`TList`) or `TObjectList` (`TObjectList`) respectively and using pointers to the items as data stored by the nodes. That simplifies synchronization and is actually closer to the model-view patterns. In your case you'd need two collections so long the nodes can be edited without affecting the first tree (that can be even optimized by creating collection that will contain references to dragged node data and when those get changed will be contained in the second collection). But it depends on usage. – Victoria Jun 02 '17 at 19:03
  • It's a pity, but thanks anyway, I'll try to use your idea with lists. But something tells me that it can be done without them. Collegues, does anybody have any other suggestions? – Deluxe Jun 03 '17 at 09:53
  • You must use the OnSaveNode and OnLoadNode events if you are using the OLE DragDrop – Fritzw Jun 20 '17 at 13:25

0 Answers0