?
Solved

How to Save The Listview Content?

Posted on 2010-09-11
7
Medium Priority
?
773 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
7 Comments
 
LVL 9

Accepted Solution

by:
Mahdi78 earned 668 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 664 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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 668 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

Enroll in September's Course of the Month

This month’s featured course covers 16 hours of training in installation, management, and deployment of VMware vSphere virtualization environments. It's free for Premium Members, Team Accounts, and Qualified Experts!

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
In this video you will find out how to export Office 365 mailboxes using the built in eDiscovery tool. Bear in mind that although this method might be useful in some cases, using PST files as Office 365 backup is troublesome in a long run (more on t…

719 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