使用XML保存和加载Treeview [英] Saving and Loading Treeview using XML

查看:116
本文介绍了使用XML保存和加载Treeview的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆