29. maj 2008 - 13:16Der er
18 kommentarer og 1 løsning
xml treevire og listview
Hej
Jeg er løbet ind i et problem, jeg bruger delphi 7 personal og har brug for at gemme og hente data fra en xml fil til et treeview og et list view
min ide var at lave en xml fil ca sådan her <data> <treeview> <item1></item1> <data1></data1> <data2></data2> <data3></data4> <item2></item1> <data1></data1> <data2></data2> <data3></data4> </treeview> <listview> <item1></item1> <data1></data1> <data2></data2> <data3></data4> <data4></data4> </listview> </data>
I kan se idéen selv om den ikke er bygget rigtigt op :)
Treeview struktur Item1 Data Data Data Item2 Data Data Data
Har du en schema-fil? Med den kan du generere en unit der håndterer ovenstående data som arrays du bare skal pløje igennem. Den kan du eventuelt nedarve til en klasse der ordner putter <itemX>-nodes i hver sin liste. Ikke noget med besværlige traverseringer af en dum XML-struktur.
Hvis XML ikke er et krav så stream' data i stedet. Det er meget lettere (og hurtigere). Har flere gange skrevet indlæg hvor jeg bruger TReader og TWriter til at gemme den slags data.
Hvis data kommer fra programmet og andre programmer ikke skal drage umiddelbar nytte deraf, så er XML overkill. Streaming er vejen frem; hvorfor ikke gøre som alle andre programmer? Fordelen ved at streame manuelt i forhold til den indbyggede (SaveToFile et al) er, at du har fuld styr over processen og over hvad der gemmes. Når først klasserne er på plads kan du tilføje, tilrette og gøre ved. Desuden kan du med en simpel versionsstyring gøre filerne bagudkompatibele, dvs. du kan stadig læse en datafil fra program v. 1.0 i program v. 12.01b
hrc, din løsning lyder som en god ide Jeg har set på det og har fået lært et par ting Hvad jeg har gjort pt er at lave et par record, som jeg stremer til og fra en TMemoryStream som gemmes til en fil
Men hvordan får jeg styr på om det er en listview eller en treeview record, jeg har lavet 2 records en til hver, men hvordan kan jeg gemme dem i samme fil ?
PDataList= ^TDataList; TDataList= Record data1 : string[15]; data2 : integer;
Jeg bryder mig ikke om at bruge records længere. Da klasser kom til synes jeg de blev forældede. De er hurtigere men i nedenstående bruger jeg den skjulte "string"-klasse som ikke er bundet til max. 255 karakterer.
Jeg ville nok ende med et klasseorgie ... som nedenstående. Det er måske skudt over målet, har end ikke tjekket eksemplet i det link jeg lagde. Det var bare en idé der udviklede sig. Jeg har to baseklasser hvori jeg definerer nogle fælles metoder og fra disse nedarver jeg resten. Der er en klasse til et TTreeView og et TTListView. Hvis du vil have dem samlet laver du bare en klasse:
TMyData = class private fListViewList: TListViewList; fTreeViewList: TTreeViewList; public constructor Create; // Opretter klasserne her destructor Destroy; override; // Sletter dem her end;
... Nok om det. Her er unitten
unit UData;
interface
uses SysUtils, Classes, ComCtrls, ContNrs;
type TBaseData = class private public procedure SaveToWriter(aWriter: TWriter); virtual; abstract; procedure LoadFromReader(aReader: TReader); virtual; abstract; end;
procedure TListViewData.SaveToWriter(aWriter: TWriter); begin aWriter.WriteString(fName); aWriter.WriteInteger(fValue); end;
{ TMyList }
procedure TListViewList.FillListView(aListView: TListView); var i: integer; begin aListView.Items.BeginUpdate; try aListView.Items.Clear; for i := 0 to Count - 1 do Items[i].FillListItem(aListView.Items.Add); finally aListView.Items.EndUpdate; end; end;
function TListViewList.GetListViewData(const aIndex: integer): TListViewData; begin result := inherited Items[aIndex] as TListViewData; end;
procedure TListViewList.LoadFromStream(aStream: TStream); var i: integer; Reader: TReader; begin Reader := TReader.Create(aStream,1024); try for i := 0 to Reader.ReadInteger - 1 do Add(TListViewData.Create(Reader)); finally Reader.Free; end; end;
{ TTreeVievList }
procedure TTreeVievList.FillTreeView(aTreeView: TTreeView); var i: integer; begin aTreeView.Items.BeginUpdate; try aTreeView.Items.Clear; for i := 0 to Count - 1 do Items[i].FillTreeNode(aTreeView.Items.Add(nil,'')); finally aTreeView.Items.EndUpdate; end; end;
function TTreeVievList.GetTreeViewData(const aIndex: integer): TTreeViewData; begin result := inherited Items[aIndex] as TTreeViewData; end;
procedure TTreeVievList.LoadFromStream(aStream: TStream); var i: integer; Reader: TReader; begin Reader := TReader.Create(aStream,1024); try for i := 0 to Reader.ReadInteger - 1 do Add(TTreeViewData.Create(Reader)); finally Reader.Free; end; end;
{ TBaseList }
procedure TBaseList.LoadFromFile(const aFilename: string); var FileStream: TFileStream; begin FileStream := TFileStream.Create(aFilename,fmOpenRead); try LoadFromStream(FileStream); finally FileStream.Free; end; end;
procedure TBaseList.SaveToFile(const aFilename: string); var FileStream: TFileStream; begin FileStream := TFileStream.Create(aFilename,fmCreate); try SaveToStream(FileStream); finally FileStream.Free; end; end;
procedure TBaseList.SaveToStream(aStream: TStream); var i: integer; Writer: TWriter; begin Writer := TWriter.Create(aStream,1024); try Writer.WriteInteger(Count); for i := 0 to Count - 1 do (Items[i] as TBaseData).SaveToWriter(Writer); Writer.FlushBuffer; finally Writer.Free; end; end;
Jeg ved godt det fylder en del, men der bruges nogle objektorienterede teknikker som jeg håber andre vil synes lærerige. Bruger bl.a. abstrakte metoder. Kunne overveje at lave det med Interfaces i stedet men jeg synes de er besværligere.
Selvom dit oplæg ikke vil fylde helt så meget er det min påstand, at der vil være større muligheder for at noget fejler.
Kunne overveje at lave ovenstående som en nedarving af TCollectionData og TCollectionItems men jeg kan godt lide TObjectLists simplicitet.
Jeg har set lidt på det, og er kommet til at se et problem som jeg også vil få med records. Problemet er at data til treeview kan have 0 – xxx subnotes, hvordan vil man lettest overkomme dette? Man kan slf lave et objekt pr subnote og soter dem når man indsætter dem i treeview, men dette vil være langsomt og ikke særlig smart
Enten kan du erstatte Value1 og Value2 med en TStringList. Så er træets niveau 0 og 1 på plads. Skal du have flere niveauer kan du lade data-klassen være en liste som kan indeholde et vilkårligt antal niveauer og items. Måske skulle data-klasserne bare nedarve fra TTreeNode?
TTreeViewData = class(TBaseData) private public end;
...
var Index: integer; TreeViewData: TTreeViewData; begin TreeViewData := TTreeViewData.Create('Hello world'); Index := TreeViewData.Add(TTreeViewData.Create('Hi. How are you?'); TreeViewData[Index].Add(TTreeViewData.Create('Fine thanks')); osv. end;
Man kan i øvrigt skrive ovenstående sådan her, men det er ligesom læsbarheden forsvinder :-):
TreeViewData := TTreeViewData.Create('Hello world').Add(TTreeViewData.Create('Hi How are you?').Add(TTreeViewData.Create('Fine thanks')));
Det må i undskylde, jeg var ved og ryde op og fik lukket dette ved en fejl Jeg har oprettet en ny spm så i kan få jeres point, jeg har brugt hrc's løsning og lært en del af det selv om jeg ikke er helt færdig så ser det godt ud http://www.eksperten.dk/spm/834756
/MB
/MB
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.