2013-09-02 2 views
4

ПримечаниеСохранение и загрузка Treeview с помощью XML

Извините заранее за длинный пост, я, хотя было бы лучше поставить как можно больше информации о как можно скорее, чем заполнить пробелы, когда это необходимо.

Заметьте, что я также отметил это как Delphi и занимаюсь собственным делом и использую Delphi XE. Теперь я использую Lazarus в качестве своей основной IDE, я просто не могу позволить себе покупать более новые версии Delphi, и теперь Lazarus становится более стабильным. имеет смысл сделать переход на Лазарь.

Для этого вопроса я включил приложение zip с источником проекта, хотя он написан в Лазаресе, и это действительно поможет с вопросом, который у меня есть, поэтому комментарии в первом абзаце.


Обзор

Onto вопрос, у меня есть объект, который является владельцем нескольких классов как TLists.

Я представляю эти данные в Treeview, и нет никакого способа узнать, сколько уровней и узлов будет присутствовать в дереве, поскольку они динамически создаются во время выполнения. Одно ограничение, которое я установил, заключается в том, что узлы верхнего уровня будут фиксированными, то есть они не могут быть удалены или переименованы - это то, что я назову RootGroups.

Treeview будет заполнен элементами и группами, каждый узел, добавленный в Treeview, будет иметь свой собственный объект, назначенный данным, чтобы правильно идентифицировать каждый элемент. Я собираюсь показать пример экрана в настоящее время, чтобы дать лучшее представление перед продолжением:

enter image description here

Как вы можете видеть, у меня есть два верхних большинство узлов, Object1Root и Object2Root. Если вы заметили кнопки справа, они позволяют добавлять группу и элементы в Treeview, но они становятся отключенными, если они не принадлежат к той части Treeview. Например, вы не можете добавить Object2Group или Object2Item под Object1Root.

В принципе все в Treeview имеет свой собственный указатель на объект. Каждый объект я получаю из базового объекта. Этот базовый объект имеет свойство для хранения положения, где он находится в 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; 

Я тогда вывожу мои другие классы от базового объекта, как это:

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 файла на основе первого изображения будет выглядеть следующим образом:

enter image description here

Примечание: В SomeVal части не важно, так как я никогда не потрудился ничего писать к объектам.

Действительно, что мне нужно сделать, это сохранить в XML так же, как показано Treeview. Я не слишком знаком с 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> 

Тогда я мог бы загрузить TreeView с XML. Проблема в том, что я действительно знаю, как сохранить XML, как я в настоящее время, я знаю, что какая-то рекурсия и т. Д. Необходимы, и именно здесь я буду бороться и, в частности, перестроить дерево из XML-файла.

Attachment

Мне потребовалось несколько часов, чтобы раздеться мой фактический код проекта в качестве примера, который легче читать и понимать, что написано в Lazarus и использует библиотеку OmniXML, я включили исходные единицы без файла проекта.

скачать здесь (пароль StackOverflow): http://www34.zippyshare.com/v/16401041/file.html

В конце концов, мой вопрос:

  • Как сохранить XML с правильной структурой иерархии.
  • Как загрузить XML и перестроить Treeview так, как это было до сохранения.

Большое спасибо.

+0

Вы спрашивали об этом уже дважды. Возможно, удалить другую Q. –

+2

@DavidHeffernan жаль, что это была настоящая ошибка. Я забыл ее удалить. Мне нужно было выпить, потратив возраст, написание вопроса и составление примеров. –

+1

ОК, я понимаю. Это намного лучше Q, +1. –

ответ

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

У меня было время для работы с этим сейчас, и, кроме внесения некоторых изменений в работу с OmniXML, это работает так, как я надеялся. Мне все еще нужно изменить много вещей, чтобы хранить мои объекты в узлах и читать их, но теперь я принимаю ваш ответ, и если у меня возникнут проблемы, я отправлю новый вопрос. Большое спасибо за ваш блестящий ответ. –

+0

Спасибо. Извините за неудобства из-за моего отсутствия опыта работы с OmniXML. – bummi

+1

определенно никаких неудобств вообще, на самом деле внесение этих небольших изменений помогло мне понять немного лучше, я новичок во всем этом материале XML. –

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" /> 
Смежные вопросы