微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用XML保存和加载Treeview

注意

对于这篇长篇文章,我很抱歉,尽管最好尽量提供尽可能多的信息,而不是在需要时填补空白.

注意虽然我已经将它标记为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>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。