注意
对于这篇长篇文章,我很抱歉,尽管最好尽可能多地提供相关信息,而不是在需要时填补空白。
注意虽然我已经将它标记为Delphi并且拥有并仍然使用Delphi XE我现在使用Lazarus作为我的主要IDE,我根本买不起更新的Delphi版本,现在Lazarus变得更加稳定了我觉得切换到拉撒路。
对于这个问题,我已经包含了一个带有项目源的zip附件,虽然用Lazarus写的,但它确实对我的问题有帮助,因此在第一段中给出了评论。
概述
在这个问题上,我有一个拥有多个类作为TList的对象。
我在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 Object派生我的其他类,如下所示:
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,然后为每个对象分配节点数据,如Parent,Level,Index等。输出XML文件基于第一个图像看起来像这样:
注意:SomeVal部分并不重要,因为我从不打算向对象写任何东西。
我应该做的就是在表示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
最终我的问题是:
非常感谢。
答案 0 :(得分:5)
作为进一步发展的原始草案。
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>
答案 1 :(得分:0)
我自己的尝试。使用MSXML 6.0类型库。不太花哨,但似乎做了这件事。
unit ttreexml;
// treeview to XML, XML to treeview by Glenn1234,
// may be used with proper credit given
interface
uses msxml2_tlb, comctrls, dialogs, sysutils;
type
// saves TTreeView as XML file.
TTreeViewToXML = class
private
doc: IXMLDOMDocument;
FTree: TTreeView;
procedure XMLPopulate(BaseNode: TTreeNode; DataItem: IXMLDOMelement);
Public
Constructor Create(Tree: TTreeView);
procedure SaveToFile(filename: string);
end;
// loads TTreeView from XML file
TXMLToTreeView = class
private
doc: IXMLDOMDocument;
FTree: TTreeView;
procedure XMLLoad(BaseItem: TTreeNode; DataItem: IXMLDOMNode);
Public
Procedure XMLToTree(Tree: TTreeView; Const FileName: String);
end;
implementation
constructor TTreeViewToXML.Create(Tree: TTreeView);
begin
FTree := Tree;
end;
procedure TTreeViewToXML.XMLPopulate(BaseNode: TTreeNode; DataItem: IXMLDOMelement);
var
SubItem: IXMLDOMElement;
selnode: TTreeNode;
begin
SelNode := BaseNode;
while selnode <> nil do
begin
if SelNode.HasChildren then
begin
SubItem := doc.CreateElement('Group');
SubItem.setAttribute('Value', SelNode.Text);
DataItem.AppendChild(SubItem);
XMLPopulate(SelNode.GetFirstChild, SubItem);
end
else
begin
SubItem := doc.CreateElement('Item');
SubItem.setAttribute('Value', SelNode.Text);
DataItem.AppendChild(SubItem);
end;
SelNode := SelNode.GetNextChild(SelNode);
end;
end;
procedure TTreeViewToXML.SaveToFile(filename: string);
var
topnode: IXMLDOMElement;
selnode: TTreeNode;
begin
//create DOM document instance
doc := CoDOMDocument.Create;
doc.async := false;
//------------------------------------------------------------------------------
topnode := doc.createElement('TreeView');
doc.appendChild(topnode);
selnode := FTree.Items.GetFirstNode;
XMLPopulate(SelNode, topnode);
doc.save(FileName);
end;
procedure TXMLToTreeView.XMLLoad(BaseItem: TTreeNode; DataItem: IXMLDOMNode);
var
item1, item2: IXMLDOMNode;
attr: IXMLDOMNamedNodeMap;
CurrItem: TTreeNode;
begin
Item1 := DataItem;
CurrItem := nil; // compiler complains if I don't do this
while Item1 <> nil do
begin
attr := item1.attributes;
item2 := attr.nextNode;
while item2 <> nil do
begin
CurrItem := FTree.Items.AddChild(BaseItem, Item2.NodeValue);
item2 := attr.nextNode;
end;
if item1.nodename = 'Group' then
XMLLoad(CurrItem, Item1.Get_firstChild);
Item1 := Item1.Get_nextSibling;
end;
end;
Procedure TXMLToTreeView.XMLToTree(Tree: TTreeView; Const FileName: String);
var
item1: IXMLDOMNode;
begin
//create DOM document instance
doc := CoDOMDocument.Create;
doc.async := false;
FTree := Tree;
//------------------------------------------------------------------------------
if doc.load(FileName) then
begin
FTree.Items.BeginUpdate;
FTree.Items.Clear;
Item1 := doc.documentElement.Get_firstChild;
XMLLoad(nil, Item1);
FTree.Items.EndUpdate;
end
else
begin
MessageDlg(Format ('Error loading XML document.'#13 +
'Error number: %d'#13 +
'Reason: %s'#13 +
'Line: %d'#13 +
'Column: %d', [doc.parseError.errorCode,
doc.parseError.reason,
doc.parseError.line,
doc.parseError.linePos]), mtError, [mbOK], 0);
end;
end;
end.
快速示例XML输出:
- <Group Value="Delphi 3">
- <Group Value="BIN">
<Item Value="BOWF520.DLL" />
<Item Value="BOWFVC.DLL" />
<Item Value="BRC32.EXE" />
<Item Value="BRCC32.EXE" />
. . .
<Item Value="DELPHI32.EXE" />
<Item Value="DELPHIMM.DLL" />
. . .
</Group>
<Item Value="DeIsL1.isu" />
- <Group Value="Demos">
- <Group Value="ACTIVEX">
- <Group Value="DELCTRLS">
<Item Value="ABOUT1.DFM" />