Solved

How to Save The Listview Content?

Posted on 2010-09-11
7
704 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 1

Expert Comment

by:peterkiers
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

772 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

11 Experts available now in Live!

Get 1:1 Help Now