Salvando e carregando Treeview usando XML

Nota

Desculpem-me antecipadamente pelo longo post, embora seja melhor colocar o máximo de informação possível, em vez de preencher as lacunas quando necessário.

Note, embora eu tenha marcado isso como Delphi e também possuo e ainda uso o Delphi XE Agora estou usando o Lazarus como meu IDE primário, simplesmente não posso comprar as novas versões do Delphi e agora o Lazarus está se tornando mais estável faz sentido para mim para fazer a mudança para o Lazarus.

Para esta pergunta eu incluí um anexo com fonte de projeto, embora escrito no Lazarus ele realmente ajudará com a questão que tenho, daí os comentários no primeiro parágrafo.

visão global

Sobre a questão, eu tenho um objeto que possui várias classes como TLists.

Eu represento esses dados em uma Treeview e não há como saber quantos níveis e nós estarão presentes na árvore, pois eles são criados dinamicamente no tempo de execução. Uma limitação que defini é que os nós de nível superior serão corrigidos, o que significa que eles não podem ser excluídos ou renomeados - isso é o que chamarei de RootGroups.

A Treeview será preenchida com itens e grupos, cada nó incluído na Treeview terá seu próprio Objeto atribuído aos dados para identificar cada item corretamente. Vou mostrar uma captura de tela de exemplo agora para ter uma ideia melhor antes de prosseguir:

Como você pode ver, eu tenho os dois principais nós,Object1Root eObject2Root. Se você observar os botões à direita, eles permitirão adicionar grupo e itens à Treeview, mas eles serão desativados se não pertencerem a essa parte da Treeview. Por exemplo, você não pode adicionarObject2Group ouObject2Item debaixoObject1Root.

Basicamente tudo no Treeview tem seu próprio ponteiro para um objeto. Cada objeto estou derivando de um objeto base. Este Objeto Base tem propriedades para armazenar a posição de onde ele é encontrado na Treeview, assim:

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;

Eu posso derivar minhas outras classes do Objeto Base, assim:

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; 

oObjeto Principal que mantém todas essas classes se parece com isso:

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;

Quando eu salvar oObjeto Principal para XML I primeiro iterar todo o TreeView e, em seguida, atribuir a cada objeto os dados do nó, como pai, nível, índice, etc O arquivo XML de saída com base na primeira imagem ficaria assim:

Nota: As partes do SomeVal não são importantes, pois nunca me preocupei em escrever nada para os Objetos.

Realmente o que devo fazer é salvar no XML, assim como a TreeView é representada. Eu não estou muito familiarizado com XML como eu ainda estou começando a lidar com isso, mas acho que a saída deve ser algo como isto: (escrito no bloco de notas)

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

Então eu poderia carregar o TreeView do XML. O problema é que eu realmente só sei como salvar o XML como eu sou atualmente, eu sei que algum tipo de recursão, etc, é necessário e é aí que eu iria lutar e particularmente reconstruir a árvore do arquivo XML.

Anexo

Demorei algumas horas para despir meu código de projeto real em um exemplo que é mais fácil de ler e entender, está escrito no Lazarus e usa oBiblioteca OmniXML, Incluí apenas as unidades de origem no arquivo de projeto.

Baixe aqui (a senha é stackoverflow):http://www34.zippyshare.com/v/16401041/file.html

Em última análise, minha pergunta é:

Como salvar em XML com estrutura de hierarquia correta.Como carregar o XML e reconstruir a Treeview para exatamente como era antes de salvar.

Muito Obrigado.

questionAnswers(2)

yourAnswerToTheQuestion