Einzelnen Beitrag anzeigen

Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#14

Re: VirtualStringTree: Speicherleck

  Alt 28. Okt 2009, 15:40
Ich schlage dir mal etwas vor:
Delphi-Quellcode:
type

  TBasicCollection=class;

  //Grundlegende abstrakte Klasse für einen Knoten (egal welcher)
  TBasicCollectionItem=class(TCollectionItem)
     Destructor Destroy; override;
    private
     FChildCollection:TBasicCollection; //Für Kindknoten oder nil wenn keine Kinder
    public
     function GetVSTBezeichner:string; virtual; abstract; //Text für VST (wird je nach Ableitung anders implementiert)
     property ChildCollection:TBasicCollection read FChildCollection;
     procedure Init(const Param:string); virtual; abstract; //Param bekommt einen Teilstring von CSettingsTree
  end;

  TBasicCollection=class(TCollection)
   private
    function GetBasicCollectionItem(index:Integer): TBasicCollectionItem;
   public
    property BasicCollectionItem[index:Integer]:TBasicCollectionItem
      read GetBasicCollectionItem;
    procedure Init(const Param:string);
  end;


  //CollectionItem für die Knoten (Verzeichnisse und Benutzerhinweise)
  TCollectionItemA=class(TBasicCollectionItem)
   private
    FName: string; //hier nur Name merken
    procedure SetName(const Value: string);
   public
    function getVSTBezeichner:string; override; //result:=FName;
    property Name:string read FName write SetName;
    procedure Init(const Param:string); override;
  end;

  //CollectionItem für die anderen Knoten
  TCollectionItemB=class(TBasicCollectionItem)
   private
    FAuthReq: Integer;
    FxID: Integer;
    FText: string;
    procedure SetAuthReq(const Value: Integer);
    procedure SetText(const Value: string);
    procedure SetxID(const Value: Integer);
   public
    function getVSTBezeichner:string; override; //result:=Ftext;
    property xID:Integer read FxID write SetxID;
    property AuthReq:Integer read FAuthReq write SetAuthReq;
    property Text:string read FText write SetText;
    procedure Init(const Param:string); override;
  end;


implementation

{$R *.dfm}

//gibt den Pos-ten Teilstring in den spitzen Klammern zurück (ohne Beachtung von inneren Klammern)
function GetStringPart(const Str: string;
  Pos: Integer): String;
var aPos,ePos:Integer;
    len:Integer;
    countopen:Integer;
begin
  len:=length(str);
  aPos:=0;
  countopen:=0;
  while Pos>0 do
  begin
    inc(aPos);
    while (aPos<=len)and((Str[aPos]<>'<')or(countopen>0)) do
    begin
      case Str[aPos] of
        '<': inc(countopen);
        '>': dec(countopen);
      end;
      inc(aPos);
    end;
    inc(countopen);
    dec(Pos);
  end;

  ePos:=aPos+1;
  countopen:=0;
  while (ePos<=len)and((Str[ePos]<>'>')or(countopen>0)) do
  begin
    case Str[ePos] of
      '<': inc(countopen);
      '>': dec(countopen);
    end;
    inc(ePos);
  end;

  result:=copy(str,aPos+1,ePos-aPos-1);
end;



{ TBasicCollection }

function TBasicCollection.GetBasicCollectionItem(index:Integer): TBasicCollectionItem;
begin
  result:=items[index] as TBasicCollectionItem;
end;

procedure TBasicCollection.Init(const Param: string);
var i:Integer;
    Part:string;
    BasicCollectionItem:TBasicCollectionItem;
begin
  i:=1;
  Part:=GetStringPart(Param,i);
  while Part<>'do
  begin
    BasicCollectionItem:=Add as TBasicCollectionItem;
    BasicCollectionItem.Init(Part);
    inc(i);
    Part:=GetStringPart(Param,i);
  end;
end;


{ TBasicCollectionItem }

destructor TBasicCollectionItem.Destroy;
begin
  FChildCollection.Free;
  inherited;
end;

{ TCollectionItemA }

function TCollectionItemA.getVSTBezeichner: string;
begin
  result:=FName;
end;

procedure TCollectionItemA.Init(const Param: string);
begin
  Name:=GetStringPart(Param,1);
  FChildCollection:=TBasicCollection.Create(TCollectionItemB);
  FChildCollection.Init(GetStringPart(Param,2));
end;

procedure TCollectionItemA.SetName(const Value: string);
begin
  FName := Value;
end;


{ TCollectionItemB }

function TCollectionItemB.getVSTBezeichner: string;
begin
  result:=FText;
end;


procedure TCollectionItemB.Init(const Param: string);
begin
  xID:=StrToInt(GetStringPart(Param,1));
  AuthReq:=StrToInt(GetStringPart(Param,2));
  Text:=GetStringPart(Param,3);
end;

procedure TCollectionItemB.SetAuthReq(const Value: Integer);
begin
  FAuthReq := Value;
end;

procedure TCollectionItemB.SetText(const Value: string);
begin
  FText := Value;
end;

procedure TCollectionItemB.SetxID(const Value: Integer);
begin
  FxID := Value;
end;
In der Anwendung:
Delphi-Quellcode:
//habe mal die Konstante etwas geändert; macht das Parsen einfacher;
//wäre aber auch mit deinem String gegangen
const CSettingsTree='<'+
                    ' <Verzeichnisse>'+
                    ' <'+
                    ' <'+
                    ' <10101><0><Archivdaten>'+
                    ' >'+
                    ' <'+
                    ' <10102><0><Tempöräre Daten>'+
                    ' >'+
                    ' >'+
                    '>'+
                    '<'+
                    ' <Benutzerhinweise>'+
                    ' <'+
                    ' <'+
                    ' <10201><0><Neue Daten vorhanden>'+
                    ' >'+
                    ' <'+
                    ' <10202><0><Irgendetwas>'+
                    ' >'+
                    ' >'+
                    '>';

type
 TForm1 = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    procedure FormCreate(Sender: TObject);
    procedure VirtualStringTree1GetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FDaten:TBasicCollection;
    procedure vstInit(Node:PVirtualNode;BasicCollection:TBasicCollection);
  public
    { Public-Deklarationen }
  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDaten:=TBasicCollection.Create(TCollectionItemA);
  FDaten.Init(CSettingsTree); //CSettingsTree in die entsprechende Collectionstruktur umformen
  vstInit(nil,FDaten); //rekursiv den VST erstellen
end;

procedure TForm1.vstInit(Node: PVirtualNode;
  BasicCollection: TBasicCollection);
var i:Integer;
    BasicCollectionItem:TBasicCollectionItem;
    cNode:PVirtualNode;
begin
  for i:=0 to BasicCollection.Count-1 do //für alle items in der Collection
  begin
    BasicCollectionItem:=BasicCollection.BasicCollectionItem[i]; //getitem
    cNode:=VirtualStringTree1.AddChild(Node,BasicCollectionItem); //Knoten erstellen
    if assigned(BasicCollectionItem.ChildCollection) then //wenn Kinder vorhanden
      vstInit(cNode,BasicCollectionItem.ChildCollection); //Kindknoten rekursiv anlegen
  end;
end;

procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
begin
  //egal welcher Knoten, überall befinden sich Ableitungen von TBasicCollectionItem in NodeData
  //und alle haben die Methode GetVSTBezeichner überschrieben
  Celltext:=
   (TObject(Sender.GetNodeData(Node)^)as TBasicCollectionItem).getVSTBezeichner;
end;

//hier noch ein Beispiel, wie man weitere Informationen je Knoten bekommt
procedure TForm1.VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
var obj:TObject;
begin
  obj:=TObject(Sender.GetNodeData(Node)^);
  if obj is TCollectionITemA then
    showmessage('Root')
  else
  if obj is TCollectionitemB then
    showmessage(Format('ID: %d; AuthReq: %d; Text: %s',
      [TCollectionItemB(obj).xID,
       TCollectionItemB(obj).AuthReq,
       TCollectionItemB(obj).Text]));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FDaten.Free;
end;
Aus der Konstante wird folgende Collection aufgebaut:
Code:
TBasicCollection
|
+-TCollectionItemA(Verzeichnisse)
| |
| +-TBasicCollection
|   |
|   +--TCollectionItemB(10101|0|Archivdaten)
|   +--TCollectionItemB(10102|0|Temporäre Daten)
|
+-TCollectionItemA(Benutzerhinweise)
  |
  +-TBasicCollection
    |
    +--TCollectionItemB(10201|0|Neue Daten vorhanden)
    +--TCollectionItemB(10202|0|Irgendetwas)
Damit kannst du quasi in BasicCollectionItem alles das implementieren, was du für den VST brauchst (neben dem NodeText kann das durchaus auch noch Checkstate o.ä. sein, was man recht simpel über die Ereignisse des VST mit setzen kann.
Die Ableitungen von BasicCollectionItem berherbegen dann zusätzliche Informationen zum Knoten.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat