Solved

How to Save The Listview Content?

Posted on 2010-09-11
7
719 Views
Last Modified: 2012-08-14
Hi, I've used a while back a simple way of saving/loading the listview contents and all of its columns and items etc.

how is this possible?
0
Comment
Question by:eNarc
  • 4
  • 2
7 Comments
 
LVL 9

Accepted Solution

by:
Mahdi78 earned 167 total points
ID: 33655026
save and load data as binary data
drag 2 TButton and TListview and use the following code


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ListView1: TListView;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  F: TFileStream;
begin
  F := TFileStream.Create(ExtractFilePath(Application.ExeName)+ 'ListView.dat', fmOpenRead or fmShareDenyWrite);
  try
    F.ReadComponent(ListView1);
  finally
    F.Free;
  end;
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  F: TFileStream;
begin
  F := TFileStream.Create(ExtractFilePath(Application.ExeName)+ 'ListView.dat', fmCreate or fmShareCompat);
  try
    F.WriteComponent(ListView1);
  finally
    F.Free;
  end;
end;

end.

Open in new window

0
 
LVL 1

Assisted Solution

by:peterkiers
peterkiers earned 166 total points
ID: 33658365
Hi, I have this in my own code and it works great for me.

Greetings, Peter Kiers
procedure TMainForm.FormCreate(Sender: TObject);

begin

  if FileExists('Data1\Glucose.sav') then LoadListViewToFile(lstvGlucose, 'Data1\Glucose.sav');

  if FileExists('Data1\Bolus.sav') then LoadListViewToFile(lstvBolus, 'Data1\Bolus.sav');

  if FileExists('Data1\FoodDB.sav') then LoadListViewToFile(lstvFoodDB, 'Data1\FoodDB.sav');

  if FileExists('Data1\OwnDB.sav') then LoadListViewToFile(lstvOwnDB, 'Data1\OwnDB.sav');

end;



procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);

begin

  SaveListViewToFile(lstvGlucose, 'Data1\Glucose.sav');

  SaveListViewToFile(lstvBolus, 'Data1\Bolus.sav');

  SaveListViewToFile(lstvFoodDB, 'Data1\FoodDB.sav');

  SaveListViewToFile(lstvOwnDB, 'Data1\OwnDB.sav');

end;



procedure TMainForm.LoadListViewToFile(AListView: TListView; sFileName: string);

var

  F: TFileStream;

  IdxItem, IdxSubItem, IdxImage: Integer;

  W, ItemCount, SubCount: Word;

  pText: PChar;

  PTemp: PChar;

  MySignature: array [0..2] of Char;

  sExeName: string;

begin

  with AListView do

  begin

    ItemCount := 0;

    SubCount  := 0;

    sExeName := ExtractFileName(sFileName);

    if not FileExists(sFileName) then

    begin

      MessageBox(Handle, PChar(Format(Msg1, [sExeName])), 'I/O Error', MB_ICONERROR);

      Exit;

    end;

    F := TFileStream.Create(sFileName, fmOpenRead);

    F.Read(MySignature, SizeOf(MySignature));

    if MySignature <> 'LVF' then

    begin

      MessageBox(Handle, PChar(Format(Msg2, [sExeName])), 'I/O Error', MB_ICONERROR);

      Exit;

    end;

    F.Read(ItemCount, SizeOf(ItemCount));

    Items.Clear;

    for idxItem := 1 to ItemCount do

    begin

      with Items.Add do

      begin

        F.Read(SubCount, SizeOf(SubCount));

        F.Read(IdxImage, SizeOf(IdxImage));

        ImageIndex := IdxImage;

        F.Read(w, SizeOf(w));

        pText := StrAlloc(w + 1);

        pTemp := StrAlloc(w + 1);

        F.Read(pTemp^, W);

        StrLCopy(pText, pTemp, W);

        Caption := StrPas(pText);

        StrDispose(pTemp);

        StrDispose(pText);

        if SubCount > 0 then

        begin

          for idxSubItem := 1 to SubCount do

          begin

            F.Read(w, SizeOf(w));

            pText := StrAlloc(w + 1);

            pTemp := StrAlloc(w + 1);

            F.Read(pTemp^, W);

            StrLCopy(pText, pTemp, W);

            Items[idxItem - 1].SubItems.Add(StrPas(pText));

            StrDispose(pTemp);

            StrDispose(pText);

          end;

        end;

      end;

    end;

    F.Free;

  end;

end;



procedure TMainForm.SaveListViewToFile(AListView: TListView; sFileName: string);

var

  idxItem, idxSub, IdxImage: Integer;

  F: TFileStream;

  pText: PChar;

  sText: string;

  W, ItemCount, SubCount: Word;

  MySignature: array [0..2] of Char;

begin

  with AListView do

  begin

    ItemCount := 0;

    SubCount  := 0;

    MySignature := 'LVF';

    F := TFileStream.Create(sFileName, fmCreate or fmOpenWrite);

    F.Write(MySignature, SizeOf(MySignature));

    if Items.Count = 0 then

      ItemCount := 0

    else

      ItemCount := Items.Count;

    F.Write(ItemCount, SizeOf(ItemCount));

    if Items.Count > 0 then

    begin

      for idxItem := 1 to ItemCount do

      begin

        with Items[idxItem - 1] do

        begin

          if SubItems.Count = 0 then

            SubCount := 0

          else

            SubCount := Subitems.Count;

          F.Write(SubCount, SizeOf(SubCount));

          IdxImage := ImageIndex;

          F.Write(IdxImage, SizeOf(IdxImage));

          sText := Caption;

          w     := Length(sText);

          pText := StrAlloc(Length(sText) + 1);

          StrPLCopy(pText, sText, Length(sText));

          F.Write(w, SizeOf(w));

          F.Write(pText^, w);

          StrDispose(pText);

          if SubCount > 0 then

          begin

            for idxSub := 0 to SubItems.Count - 1 do

            begin

              sText := SubItems[idxSub];

              w     := Length(sText);

              pText := StrAlloc(Length(sText) + 1);

              StrPLCopy(pText, sText, Length(sText));

              F.Write(w, SizeOf(w));

              F.Write(pText^, w);

              StrDispose(pText);

            end;

          end;

        end;

      end;

    end;

    F.Free;

  end;

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 33660759
Peter, your code is a bit too complex : you don't have to reserve 2 temp memory buffer to load/save one string. besides, try to use global functions for pieces of code that are alike, that makes the global logic much easier to understand. And when stream are concerned, it will be all the more evident that load/save functions are symetric if it uses elementary functions.

Always makes those functions compatible with every stream types, as it could be reused for other purpose instantly (DB blob fields for example). In my example below, it would be easy to save all your four listview in one file, because I have a set of functions to load/save one listview in a stream, whatever the stream type, and whatever we want to store in that stream before or after.

The only thing I removed from your file format is your signature, and changed the order of items properties, but you could make that compatible with your own functions easily if you wanted to replace them.

@eNarc : I have kept it simple, you can either use it as it is, or you can adapt this code easily to suit your needs. The coding style is what matters here, and if you master its principles you can adapt to whatever complex file structure with whatever components data.

NB : the load/save string basic functions can be compiled with all Delphi versions, they will adpat to ANSI or Unicode string automatically (thanks to sizeof(Char) ), but beware that Delphi versions >=2009 (Unicode) will not generate the same files than versions <2009 (ANSI), so files would be compatible only between applications compiled by the same Delphi generation.
// GLOBAL STREAM UTILS : Load/Save Int/Strings



procedure StreamWriteInt(aStream:TStream;Val:Integer);

begin

 aStream.Write(Val,sizeof(Val)); 

end;



function  StreaReadInt(aStream:TStream):Integer;

begin

 aStream.Read(Result,sizeof(Result));

end; 



procedure StreamWriteString(aStream:TStream;aString:String);

Var

 L:Integer;

begin

 L:=Length(aString);

 StreamWriteInt(L);

 if L>0 Then aStream.Write(PChar(@aString[1])^,L*sizeof(Char));

end;

 

function StreamReadString(aStream:TStream):String;

Var

 L:Integer;

begin

 L:=StreaReadInt(aStream);

 SetLength(Result,L);

 if L>0 Then aStream.Read(PChar(@Result[1])^,L*sizeof(Char));

end;



//======== Load/Save ListView in stream ========



procedure StreamWriteListView(aStream:TStream;aListView:TListView);

Var

 i,j:integer;

begin

 StreamWriteInt(aStream,aListView.Items.Count);

 for i:=0 to aListView.Items.Count-1 do

  with aListView.Items[idxItem - 1] do

   begin

    StreamWriteString(aStream,Caption);

    StreamWriteInt(aStream,ImageIndex);

    StreamWriteInt(aStream,Subitems.Count);

    for j:=0 to Subitems.Count-1 do StreamWriteString(aStream,Subitems[j]);

   end;

end;



procedure StreamReadListView(aStream:TStream;aListView:TListView);

Var

 N,i,NS,j:integer;

begin

 aListView.Clear;

 N:=StreamReadInt(aStream);

 for i:=0 to N-1 do

  with aListView.Items.Add do

   begin

    Caption:=StreamReadString(aStream);

    ImageIndex:=StreamReadInt(aStream);

    NS:=StreamReadInt(aStream,Subitems.Count);

    for j:=0 to NS-1 do Subitems.Add(StreamReadString(aStream));

   end;

end;



//======== Load/Save ONE ListView in a file ========



procedure SaveListViewToFile(AListView: TListView; sFileName: string);

var

 F: TFileStream;

begin

 F := TFileStream.Create(sFileName, fmCreate or fmOpenWrite);

 try

  StreamWriteListView(F,AListView);

 finally

  F.Free;

 end;

end;



procedure LoadListViewToFile(AListView: TListView; sFileName: string);

var

 F: TFileStream;

begin

 F := TFileStream.Create(sFileName, fmOpenRead);

 try

  StreamReadListView(F,AListView);

 finally

  F.Free;

 end;

end;

Open in new window

0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 1

Expert Comment

by:peterkiers
ID: 33662479
Hi,

I just want to say that even thou my code is longer than the code from Espasquier.
It saves and loades only the content of the listview. And therefor the file is much smaller
than when you use the code form Espasquier that saves not only the data but other stuff too.
I have test it with my application, and my code had a file of 22kb and Espasquier 44kb.

Greetings, Peter Kiers
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33663263
It does not save other stuff. Read/Write Component will certainly do, but not the code I posted if you look at it you'll see it saves exactly the same as in your functions.

If you have Delphi>=2009, it saves strings as Unicode, and (for all Delphi versions) it saves length as Integers (4 bytes) where you save them as Words (2 bytes)
so twice the size in all cases.
But it might be that you need that, if you have a VCL that uses unicode, then it is well possible you might want all possible characters saved. If not, and if you want a code that load/save strings in ANSI in all delphi version, the only thing needed is to change StreamWriteString/StreamReadString
Same goes for the string length or items count with a limit of 65535 (yes, that is reasonable enough)

At this point that is just a matter of taste, and with my coding structure, an easy thing to fix.
// load/save ints as word values 

// *** warning : values <0 and >65535 will not work properly) ***

procedure StreamWriteInt(aStream:TStream;Val:Integer);

Var

 W:Word;

begin

 W:=Val;

 aStream.Write(W,sizeof(W)); 

end;



function  StreaReadInt(aStream:TStream):Integer;

Var

 W:Word;

begin

 aStream.Read(W,sizeof(W));

 Result:=W;

end; 



// load/save strings as ANSI, not Unicode even on Delphi>=2009

// *** warning : Unicode characters that are not ANSI will be lost ***

procedure StreamWriteString(aStream:TStream;aString:ANSIString);

Var

 L:Integer;

begin

 L:=Length(aString);

 StreamWriteInt(L);

 if L>0 Then aStream.Write(PANSIChar(@aString[1])^,L);

end;

 

function StreamReadString(aStream:TStream):ANSIString;

Var

 L:Integer;

begin

 L:=StreaReadInt(aStream);

 SetLength(Result,L);

 if L>0 Then aStream.Read(PANSIChar(@Result[1])^,L);

end;

Open in new window

0
 
LVL 25

Assisted Solution

by:epasquier
epasquier earned 167 total points
ID: 33663454
Just for the record, I tested my functions set and a few typos slipped through
Here is the corrected code.
procedure StreamWriteInt(aStream:TStream;Val:Integer);

begin

 aStream.Write(Val,sizeof(Val)); 

end;



function  StreamReadInt(aStream:TStream):Integer; // Here : forgot 'm' in StreamReadInt

begin

 aStream.Read(Result,sizeof(Result));

end; 



procedure StreamWriteString(aStream:TStream;aString:String);

Var

 L:Integer;

begin

 L:=Length(aString);

 StreamWriteInt(aStream,L); // here, forgot aStream parameter

 if L>0 Then aStream.Write(PChar(@aString[1])^,L*sizeof(Char));

end;

 

function StreamReadString(aStream:TStream):String;

Var

 L:Integer;

begin

 L:=StreamReadInt(aStream);

 SetLength(Result,L);

 if L>0 Then aStream.Read(PChar(@Result[1])^,L*sizeof(Char));

end;



//======== Load/Save ListView in stream ========



procedure StreamWriteListView(aStream:TStream;aListView:TListView);

Var

 i,j:integer;

begin

 StreamWriteInt(aStream,aListView.Items.Count);

 for i:=0 to aListView.Items.Count-1 do

  with aListView.Items[i] do // here, index was wrong

   begin

    StreamWriteString(aStream,Caption);

    StreamWriteInt(aStream,ImageIndex);

    StreamWriteInt(aStream,Subitems.Count);

    for j:=0 to Subitems.Count-1 do StreamWriteString(aStream,Subitems[j]);

   end;

end;



procedure StreamReadListView(aStream:TStream;aListView:TListView);

Var

 N,i,NS,j:integer;

begin

 aListView.Clear;

 N:=StreamReadInt(aStream);

 for i:=0 to N-1 do

  with aListView.Items.Add do

   begin

    Caption:=StreamReadString(aStream);

    ImageIndex:=StreamReadInt(aStream);

    NS:=StreamReadInt(aStream);

    for j:=0 to NS-1 do Subitems.Add(StreamReadString(aStream));

   end;

end;



//======== Load/Save ONE ListView in a file ========



procedure SaveListViewToFile(AListView: TListView; sFileName: string);

var

 F: TFileStream;

begin

 F := TFileStream.Create(sFileName, fmCreate or fmOpenWrite);

 try

  StreamWriteListView(F,AListView);

 finally

  F.Free;

 end;

end;



procedure LoadListViewToFile(AListView: TListView; sFileName: string);

var

 F: TFileStream;

begin

 F := TFileStream.Create(sFileName, fmOpenRead);

 try

  StreamReadListView(F,AListView);

 finally

  F.Free;

 end;

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 33663540
Another thing : Read/Write Component works Ok without having to think much, but remember that it uses HUGE portions of code, so will be slower, will load/save everything not only relevant data, including the ClassType of your listview

so it can be a problem if you want to change the look of your application, with different properties and/or another listview component
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
IExtractImage Delphi 14 201
FMX enumerated colours 2 84
Delphi application Soap connection 5 96
QRReport  TQrmemo vertical stretching 1 42
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
Edureka is one of the fastest growing and most effective online learning sites.  We are here to help you succeed.

911 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now