В современных приложениях часто бывает необходимо отображать иерархические данные в нескольких местах с незначительными изменениями в способе отображения. Одним из способов достижения этого является использование компонента VirtualStringTree в Delphi. Однако, при использовании этого компонента может возникнуть проблема с дублированием данных, если для каждого экземпляра дерева создается отдельная копия данных.
В данной статье мы рассмотрим, как синхронизировать несколько VirtualStringTrees с одной копией данных в Delphi. Мы покажем, как создать "мастер"-дерево, которое будет ссылаться на фактические данные, и несколько "рабынь"-деревьев, которые будут ссылаться на те же данные.
Для начала давайте посмотрим на пример кода, который был представлен в контексте:
// Initialize the NodeDataSize
procedure TForm1.FormCreate(Sender: TObject);
begin
TreeMasterComponents.NodeDataSize := SizeOf(rMasterComponent);
VST.NodeDataSize := SizeOf(Pointer);
end;
// Procedure to copy the master tree to slave trees
procedure TForm1.LoadSlaveTree(ATree: TVirtualStringTree);
var Node : PVirtualNode;
procedure RecursiveCopy(SrcPNode, SrcTNode : PVirtualNode; ATree : TVirtualStringTree);
var SrcNode, TargetNode : PVirtualNode;
SrcData : PMasterComponent;
begin
SrcNode := TreeMasterComponents.GetFirstChild(SrcPNode);
while Assigned(SrcNode) do
begin
SrcData := TreeMasterComponents.GetNodeData(SrcNode);
TargetNode := ATree.AddChild(SrcTNode, SrcData);
RecursiveCopy(SrcNode, TargetNode, ATree);
SrcNode := SrcNode.NextSibling;
end;
end;
begin
ATree.BeginUpdate;
ATree.Clear;
Node := TreeMasterComponents.GetFirst(true);
while Assigned(Node) do
begin
RecursiveCopy(Node, nil, ATree);
Node := Node.NextSibling;
end;
ATree.EndUpdate;
end;
// Procedure for slave tree to getCellText
procedure TForm1.SlaveGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var Data : PMasterComponent;
begin
Data := Sender.GetNodeData(Node);
Case Column of
0: CellText := Data^.ComponentCode;
1: CellText := Data^.FullLocation;
end;
end;
В этом примере мы видим, что мастер-дерево инициализируется с размером NodeData, равным размеру структуры MasterComponent. Затем мы видим процедуру LoadSlaveTree, которая копирует мастер-дерево в каждое из раб-деревьев. Для этого используется процедура RecursiveCopy, которая рекурсивно копирует все дочерние узлы текущего узла. Наконец, мы видим процедуру SlaveGetText, которая используется раб-деревьями для получения текста ячейки для каждого узла.
Однако, как видно из примера, при текущем подходе текст не отображается в раб-деревьях. Причиной этого является то, что мы пытаемся直接 использовать указатель на структуру MasterComponent в качестве NodeData для раб-деревьев, что приводит к неправильному отображению данных.
Чтобы решить эту проблему, нам нужно создать neuen запись, которая будет содержать указатель на оригинальные данные:
type
PRefMasterComponent = ^RefMasterComponent;
RefMasterComponent = packed record
PMCData: PMasterComponent;
end;
Теперь мы можем инициализировать размер NodeData для раб-деревьев как размер этой новой записи:
Далее, нам нужно изменить процедуру LoadSlaveTree, чтобы использовать эту новую запись для хранения указателя на оригинальные данные:
procedure TForm1.LoadTree(ATree: TVirtualStringTree);
var
Node, TargetNode: PVirtualNode;
SrcData: PMasterComponent;
Data: PRefMasterComponent;
procedure RecursiveCopy(SrcPNode, TargetPNode: PVirtualNode; ATree: TVirtualStringTree);
var
Node, TargetNode: PVirtualNode;
SrcData: PMasterComponent;
Data: PRefMasterComponent;
begin
Node := TreeMasterComponents.GetFirstChild(SrcPNode);
while Assigned(Node) do
begin
SrcData := TreeMasterComponents.GetNodeData(Node);
TargetNode := ATree.AddChild(TargetPNode);
Data := ATree.GetNodeData(TargetNode);
Data.PMCData := SrcData;
RecursiveCopy(Node, TargetNode, ATree);
Node := Node.NextSibling;
end;
end;
begin
ATree.BeginUpdate;
ATree.Clear;
Node := TreeMasterComponents.GetFirst(true);
while Assigned(Node) do
begin
SrcData := TreeMasterComponents.GetNodeData(Node);
TargetNode := ATree.AddChild(nil);
Data := ATree.GetNodeData(TargetNode);
Data.PMCData := SrcData;
RecursiveCopy(Node, TargetNode, ATree);
Node := Node.NextSibling;
end;
ATree.EndUpdate;
end;
Наконец, нам нужно изменить процедуру SlaveGetText, чтобы использовать эту новую запись для получения данных:
procedure TForm1.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
Data: PRefMasterComponent;
RefData: PMasterComponent;
begin
Data := Sender.GetNodeData(Node);
RefData := Data.PMCData;
case Column of
0: CellText := RefData.ComponentCode;
1: CellText := RefData.FullLocation;
end;
end;
Теперь, когда мы изменяем данные в одном дереве, мы можем просто вызвать VST.Invalidate, чтобы отразить изменения в других деревах.
В заключение, мы рассмотрели, как синхронизировать несколько VirtualStringTrees с одной копией данных в Delphi. Мы показали, как создать "мастер"-дерево, которое будет ссылаться на фактические данные, и несколько "рабынь"-деревьев, которые будут ссылаться на те же данные. При правильном использовании этой техники можно добиться значительной экономии памяти и упрощения кода.
В данном контексте рассматривается решение задачи синхронизации нескольких деревьев VirtualStringTree с одной копией данных в Delphi, чтобы избежать дублирования данных при создании отдельной копии для каждого дерева. Для этого предлагается создать 'масте
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS