4

ノート

長い投稿で申し訳ありませんが、必要なときにギャップを埋めるのではなく、できるだけ多くの情報を掲載するのが最善だと思います.

これにも Delphi のタグを付けており、Delphi XE を所有し、現在も使用していますが、現在は Lazarus をプライマリ IDE として使用しています。新しい Delphi バージョンを購入する余裕がなく、Lazarus がより安定していることは理にかなっています。 Lazarus に切り替えます。

この質問については、プロジェクト ソースの zip 添付ファイルを含めましたが、Lazarus で書かれていますが、私が持っている質問に本当に役立つので、最初の段落にコメントがあります。


概要

質問に、TLists としていくつかのクラスを所有するオブジェクトがあります。

このデータを Treeview で表しますが、実行時に動的に作成されるため、ツリーに存在するレベルとノードの数を知る方法はありません。私が設定した 1 つの制限は、トップ レベル ノードが固定されることです。つまり、それらを削除したり、名前を変更したりすることはできません。これらは、私が RootGroups と呼ぶものです。

ツリービューにはアイテムとグループが取り込まれ、ツリービューに追加されたすべてのノードには、各アイテムを正しく識別するためにデータに割り当てられた独自のオブジェクトがあります。先に進む前に、より良いアイデアを提供するために、スクリーンショットの例を示します。

ここに画像の説明を入力

ご覧のとおり、一番上にObject1RootObject2Rootの 2 つのノードがあります。右側のボタンに気付いた場合、グループとアイテムをツリービューに追加できますが、ツリービューのその部分に属していない場合は無効になります。たとえば、 Object1Root の下に Object2Group または Object2Item を追加することできませ

基本的に、ツリービュー内のすべてのものには、オブジェクトへの独自のポインターがあります。ベースオブジェクトから派生している各オブジェクト。このベース オブジェクトには、次のように、ツリー ビュー内で見つかった位置を格納するためのプロパティがあります。

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 全体を反復処理してから、各オブジェクトに Parent、Level、Index などのノード データを割り当てます。最初の画像に基づく出力 XML ファイルは次のようになります。

ここに画像の説明を入力

注: オブジェクトに何も書き込んでいないので、SomeVal の部分は重要ではありません。

本当に私がすべきことは、ツリービューが表現されているのと同じように XML に保存することです。私はまだ 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 をロードし、ツリービューを保存前の状態に正確に再構築する方法。

どうもありがとう。

4

2 に答える 2

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" /> 
于 2013-09-03T05:23:17.683 に答える