对于这篇长篇文章,我很抱歉,尽管最好尽量提供尽可能多的信息,而不是在需要时填补空白.
注意虽然我已经将它标记为Delphi并且拥有并且仍然使用Delphi XE我现在使用Lazarus作为我的主要IDE,我根本买不起新的Delphi版本,现在Lazarus变得更稳定对我来说有意义切换到拉撒路.
对于这个问题,我已经包含了一个带项目源的zip附件,虽然用Lazarus写的,但它确实对我的问题有帮助,因此第一段中的注释也是如此.
概观
在这个问题上,我有一个拥有几个类作为TLists的Object.
我在Treeview中表示这些数据,并且无法知道树中将存在多少级别和节点,因为它们是在运行时动态创建的.我设置的一个限制是顶级节点将被修复,这意味着它们不能被删除或重命名 – 这些就是我所说的RootGroups.
Treeview将填充项目和组,添加到Treeview的每个节点都将自己的Object分配给数据,以正确识别每个项目.我现在将展示一个示例屏幕截图,以便在继续之前提供更好的主意:
如您所见,我有两个最顶层的节点,Object1Root和Object2Root.如果您注意到右侧的按钮,则它们允许将组和项添加到Treeview中,但如果它们不属于Treeview的该部分,则会被禁用.例如,您无法在Object1Root下添加Object2Group或Object2Item.
基本上Treeview中的所有内容都有自己的指向对象的指针.我从基础对象派生的每个对象.此基础对象具有存储树视图中找到位置的属性,如下所示:
type TBaseObject = class private FName: string; FGroup: string; FNodeLevel: Integer; FNodeIndex: Integer; public constructor Create(AName: string); destructor Destroy; override; published property Name: string read FName write FName; property Group: string read FGroup write FGroup; property NodeLevel: Integer read FNodeLevel write FNodeLevel; property NodeIndex: Integer read FNodeIndex write FNodeIndex; end;
然后我可以从Base对象派生我的其他类,如下所示:
type TObject1RootGroup = class(TBaseObject) public constructor Create(AName: string); destructor Destroy; override; procedure ToSave(const XMLDoc: IXMLDocument; var Root, Node: IXMLNode); end; TObject1Group = class(TBaseObject) public constructor Create(AName: string); destructor Destroy; override; procedure ToSave(const XMLDoc: IXMLDocument; var Root, Node: IXMLNode); end; TObject1Item = class(TBaseObject) private FSomeVal1: string; FSomeVal2: string; public constructor Create(AName: string); destructor Destroy; override; procedure ToSave(const XMLDoc: IXMLDocument; var Root, Node: IXMLNode); published property SomeVal1: string read FSomeVal1 write FSomeVal1; property SomeVal2: string read FSomeVal2 write FSomeVal2; end;
包含所有这些类的主对象如下所示:
type TMyObject = class(TObject) private FName: string; FObject1Groups: TList; FObject1Items: TList; FObject2Groups: TList; FObject2Items: TList; protected procedure FreeObjects; public constructor Create(AName: string); destructor Destroy; override; procedure Save(FileName: string); function Load(Filename: string): Boolean; published property Name: string read FName write FName; property Object1Groups: TList read FObject1Groups; property Object1Items: TList read FObject1Items; property Object2Groups: TList read FObject2Groups; property Object2Items: TList read FObject2Items; end;
当我将主对象保存为XML时,我首先迭代整个TreeView,然后为每个对象分配节点数据,如父级,级别,索引等.基于第一个图像的输出XML文件如下所示:
注意:SomeVal部分并不重要,因为我从未打扰过向Objects写入任何内容.
我应该做的就是在表示Treeview时保存到XML.我不太熟悉XML,因为我仍然在掌握它,但我认为输出应该是这样的:(用记事本写)
<XML Name="test.xml"> <Counts Object1Groups="3" Object1Items="5" Object2Groups="2" Object2Items="1" /> <TObject1RootGroup Name="Object1Root" Group="" NodeLevel="0" NodeIndex="0" <TObject1Item Name="Item1" Group="Object1Root" NodeLevel="1" NodeIndex="0" SomeVal1="" SomeVal2="" /> <TObject1Item Name="Item2" Group="Object1Root" NodeLevel="1" NodeIndex="1" SomeVal1="" SomeVal2="" /> <TObject1Group Name="Group1" Group="Object1Root" NodeLevel="1" NodeIndex="2" /> <TObject1Item Name="Item3" Group="Object1Root" NodeLevel="1" NodeIndex="3" SomeVal1="" SomeVal2="" /> <TObject1Group Name="Group2" Group="Object1Root" NodeLevel="1" NodeIndex="4" /> <TObject1Item Name="Item1" Group="Group2" NodeLevel="2" NodeIndex="0" SomeVal1="" SomeVal2="" /> <TObject1Group Name="Group1" Group="Group2" NodeLevel="2" NodeIndex="1" /> <TObject1Item Name="Item1" Group="Group1" NodeLevel="3" NodeIndex="0" SomeVal1="" SomeVal2="" /> <TObject2RootGroup Name="Object2Root" Group="" NodeLevel="0" NodeIndex="1" <TObject2Group Name="Group1" Group="Object2Root" NodeLevel="1" NodeIndex="0" /> <TObject2Group Name="Group2" Group="Object2Root" NodeLevel="1" NodeIndex="1" /> <TObject2Item Name="Item1" Group="Group2" NodeLevel="2" NodeIndex="0" SomeVal1="" SomeVal2="" /> </XML>
然后我可以从XML加载TreeView.问题是我现在才真正知道如何保存XML,我知道需要某种递归等等,这是我要努力的地方,特别是从XML文件重建树.
附件
我花了几个小时把我的实际项目代码拆成一个更易于阅读和理解的例子,它是用Lazarus编写的并使用OmniXML库,我只包含源单元没有项目文件.
在此处下载(密码为stackoverflow):http://www34.zippyshare.com/v/16401041/file.html
最终我的问题是:
>如何使用正确的层次结构保存到XML.
>如何加载XML并将树视图重建为保存前的确切方式.
非常感谢.
作为进一步发展的原始草案.unit TreeXML; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc, ActiveX, ComObj, ComCtrls; Type TTreeToXML = Class private FDOC: TXMLDocument; FRootNode: IXMLNode; FTree: TTreeView; procedure IterateRoot; procedure WriteNode(N: TTreeNode; ParentXN: IXMLNode); Public Constructor Create(Tree: TTreeView); Procedure SaveToFile(const fn: String); Destructor Destroy; override; End; TXMLToTree = Class private FTree: TTreeView; procedure IterateNodes(xn: IXMLNode; ParentNode: TTreeNode); Public Procedure XMLToTree(Tree: TTreeView; Const FileName: String); End; implementation { TTreeToXML } constructor TTreeToXML.Create(Tree: TTreeView); begin FTree := Tree; FDOC := TXMLDocument.Create(nil); FDOC.Options := FDOC.Options + [doNodeAutoIndent]; FDOC.Active := true; FDOC.Encoding := 'UTF-8'; FRootNode := FDOC.CreateElement('Treeview', ''); FDOC.DocumentElement := FRootNode; IterateRoot; end; Procedure TTreeToXML.WriteNode(N: TTreeNode; ParentXN: IXMLNode); var CurrNode: IXMLNode; Child: TTreeNode; begin CurrNode := ParentXN.AddChild(N.Text); CurrNode.Attributes['NodeLevel'] := N.Level; CurrNode.Attributes['Index'] := N.Index; Child := N.getFirstChild; while Assigned(Child) do begin WriteNode(Child, CurrNode); Child := Child.getNextSibling; end; end; Procedure TTreeToXML.IterateRoot; var N: TTreeNode; begin N := FTree.Items[0]; while Assigned(N) do begin WriteNode(N, FRootNode); N := N.getNextSibling; end; end; procedure TTreeToXML.SaveToFile(const fn: String); begin FDOC.SaveToFile(fn); end; destructor TTreeToXML.Destroy; begin if Assigned(FDOC) then FDOC.Free; inherited; end; { TXMLToFree } Procedure TXMLToTree.XMLToTree(Tree: TTreeView; const FileName: String); var Doc: TXMLDocument; begin FTree := Tree; Doc := TXMLDocument.Create(Application); try Doc.LoadFromFile(FileName); Doc.Active := true; IterateNodes(Doc.DocumentElement, NIL); finally Doc.Free; end; end; Procedure TXMLToTree.IterateNodes(xn: IXMLNode; ParentNode: TTreeNode); var ChildTreeNode: TTreeNode; i: Integer; begin For i := 0 to xn.ChildNodes.Count - 1 do begin ChildTreeNode := FTree.Items.AddChild(ParentNode, xn.ChildNodes[i].NodeName); IterateNodes(xn.ChildNodes[i], ChildTreeNode); end; end; end.
示例电话
procedure TForm1.Button1Click(Sender: TObject); begin With TTreeToXML.Create(TreeView1) do try SaveToFile('C:\temp\test.xml'); finally Free; end; end; procedure TForm1.Button2Click(Sender: TObject); begin With TXMLToTree.Create do try XMLToTree(TreeView2, 'C:\temp\test.xml') finally Free; end; end;
使用的XML看起来像:
<?xml version="1.0" encoding="UTF-8"?> <Treeview> <Object1Root NodeLevel="0" Index="0"> <Item1 NodeLevel="1" Index="0"/> <Item2 NodeLevel="1" Index="1"/> <Group1 NodeLevel="1" Index="2"/> <Group2 NodeLevel="1" Index="3"> <Item1 NodeLevel="2" Index="0"/> <Group1 NodeLevel="2" Index="1"> <Item1 NodeLevel="3" Index="0"/> </Group1> </Group2> </Object1Root> <Object2Root NodeLevel="0" Index="1"> <Group1 NodeLevel="1" Index="0"/> <Group2 NodeLevel="1" Index="1"> <Item1 NodeLevel="2" Index="0"/> </Group2> </Object2Root> </Treeview>