使用XML保存和加载Treeview [英] Saving and Loading Treeview using XML
问题描述
注意
对于较长的帖子,我很抱歉,尽管最好是提供尽可能多的信息,而不是在需要时填补空白.
Sorry in advance for the long post, I though it would be best to put as much information on as possible rather than fill the gaps when needed.
请注意,尽管我也将其标记为Delphi,并且确实拥有并仍在使用Delphi XE,但现在我将Lazarus用作主要的IDE,但我根本负担不起购买较新的Delphi版本,现在Lazarus变得更加稳定,这使得对我来说,改用拉撒路很有意义.
Note although I have tagged this as Delphi as well and do own and still use Delphi XE I am now using Lazarus as my primary IDE, I simply cannot afford to purchase the newer Delphi versions and now Lazarus is becoming more stable it makes sense to me to make the switch to Lazarus.
对于这个问题,我在项目源中随附了一个zip附件,尽管它是用拉撒路(Lazarus)编写的,但它确实可以解决我所遇到的问题,因此,在第一段中提供了意见.
For this question I have included a zip attachment with project source, although written in Lazarus it will really help with the question I have, hence the comments in the first paragraph.
概述
关于这个问题,我有一个拥有多个类(如TLists)的Object.
Onto the question, I have a Object that owns several classes as TLists.
我在Treeview中表示这些数据,由于在运行时动态创建树,因此无法知道树中将存在多少个级别和节点.我设置的一个限制是,顶级节点将是固定的,这意味着它们无法删除或重命名-这些就是我所说的RootGroups.
I represent this data in a Treeview and there is no way of knowing how many levels and nodes will be present in the tree as they are dynamically created at runtime. One limitation I have set is that the Top level nodes will be fixed, meaning they cannot be deleted or renamed - these are what I will call RootGroups.
Treeview将填充项目和组,添加到Treeview的每个节点都将有其自己的Object分配给数据,以正确识别每个项目.我现在将显示一个示例屏幕截图,以便在进行以下操作之前提供一个更好的主意:
The Treeview will be populated with items and groups, every node added to the Treeview will have its own Object assigned to the data to identify each item correctly. I am going to show an example screenshot now to give a better idea before carrying on:
如您所见,我有两个最上面的节点,即 Object1Root 和 Object2Root .如果您注意到右侧的按钮,它们允许将组和项目添加到树视图中,但是如果它们不属于树视图的该部分,则它们将被禁用.例如,您不能在 Object1Root 下添加 Object2Group 或 Object2Item .
As you can see I have the two top most nodes, Object1Root and Object2Root. If you notice the buttons on the right, they allow adding group and items to the Treeview but they become disabled if they don't belong in that part of the Treeview. For example you cannot add Object2Group or Object2Item under Object1Root.
基本上,树视图中的所有内容都有其自己的对象指针.我从基础对象派生的每个对象.此基础对象具有存储其在树状视图中的位置的属性,如下所示:
Basically everything in the Treeview has its own Pointer to a Object. Each Object I am deriving from a Base Object. This Base Object has properties to store the position of where it is found in the Treeview, like this:
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;
然后我可以从基础对象派生其他类,如下所示:
I can then derive my other classes from the Base Object, like this:
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;
包含所有这些类的主对象看起来像这样:
The Main Object that holds all these classes looks like this:
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,然后将每个节点数据(如Parent,Level,Index等)分配给每个Object.基于第一个文件的输出XML File图像看起来像这样:
When I save the Main Object to XML I first iterate the whole TreeView and then assign to each Object the Node data such as Parent, Level, Index etc. The output XML File based on the first image would look like this:
注意:SomeVal部分并不重要,因为我从不费心向对象写任何东西.
Note: The SomeVal parts are not important as I never bothered writing anything to the Objects.
我真正应该做的是保存到XML,就像表示Treeview一样.我对XML不太熟悉,因为我仍然会熟悉它,但是我认为输出应该看起来像这样:(用记事本编写)
Really what I should do is Save to the XML just as the Treeview is represented. I am not too familar with XML as I am still getting to grips with it, but I think the output should look something like this: (written in Notepad)
<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文件重建树.
Then I could load the TreeView from the XML. The problem is I only really know how to save the XML as I currently am, I know some kind of recursion etc is needed and this is where I would struggle, and particularly rebuilding the Tree from the XML File.
附件
我花了几个小时将我的实际项目代码分解为一个易于阅读和理解的示例,它是用Lazarus编写的,并使用了 OmniXML库,我只包含了源单位没有项目文件.
It has taken me a few hours to strip down my actual project code into an example that is easier to read and understand, it is written in Lazarus and uses the OmniXML library, I have only included the source units no project file.
在此处下载(密码为stackoverflow): http://www34.zippyshare .com/v/16401041/file.html
Download it here (the password is stackoverflow): http://www34.zippyshare.com/v/16401041/file.html
最终我的问题是:
- 如何使用正确的层次结构保存到XML.
- 如何加载XML并重建Treeview使其与保存之前的样子完全一样.
非常感谢.
推荐答案
作为进一步开发的原始草案.
As a raw draft for further development.
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>
这篇关于使用XML保存和加载Treeview的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!