Title: Direct File Access with a StringGrid
Question: From time to time we need to populate a stringgrid from a file and save to a file in a well defined structure like a record.
Here's a way to handle this with a contol class.
Answer:
Direct file access refers to that reading physical files from a local file system, and
managing the data stored in them.
These files can be simple of binary or ASCII filetyp, in our case the container is a record.
type
TAppData = record
Name: string[50];
Size: string[20];
Release: string[30];
descript: string[80];
end;
You can always use a StringGrid to display a Query Result, but if you dont need the overhead of a data aware component or you don't want a database in an embedded system for ex., direct file access is the choice.
But as a developer you are responsible for every aspect of the data access.
In short, before any data access can occur, data must be read from one or more files and stored in memory.
Memory structures are
- Streams,
- StringLists
- file of record
- arrays of records, and so on
Next we need a class, which shows sort of the principle of the PAC Architecture
(Presentation, Abstract, Control) in One. Every agent is responsible for a specific aspect of the application's functionality and consists of three elements: presentation (stringgrid), abstraction (record), and control (load from file of record to a stringgrid).
TBuildAppGrid = class (TObject)
private
aGrid: TStringGrid;
app: TAppData;
f: file of TAppData;
FaDatfile: ShortString;
protected
function GetaDatfile: ShortString;
procedure SetaDatfile(const Value: ShortString);
public
constructor initGrid(vGrid: TStringGrid; vFile: shortString);
procedure fillGrid;
procedure storeGrid;
property aDatfile: ShortString read GetaDatfile write SetaDatfile;
end;
Next, if a data file already exists, it is opened and the previously stored
data is read from it. In our case the data is written to the stringgrid (which is serving both as a memory structure for holding the data and as a visual control for navigating and editing the data). We need the constructor to pass the grid and the filename, so the class TBuildAppGrid is independent from a form namespace, no uses like frmUnit is needed, just uses QGrids as a dependency.
{
******************************** TBuildAppGrid *********************************
}
constructor TBuildAppGrid.initGrid(vGrid: TStringGrid; vFile: shortString);
begin
aGrid:= vGrid;
aDatfile:= vFile;
with aGrid do begin
ScrollBars:= ssAutoVertical;
FixedRows := 1;
FixedCols:= 0;
ColCount:= 4;
RowCount:= 10;
end;
end;
procedure TBuildAppGrid.fillGrid;
var
crow: Integer;
begin
crow := 1;
with aGrid do begin
Cells[0,0]:= 'Application Name';
ColWidths[0]:= 120;
Cells[1,0]:= 'App Size';
ColWidths[1]:= 60;
Cells[2,0]:= 'Release Date';
ColWidths[2]:= 90;
Cells[3,0]:= 'Description';
ColWidths[3]:= 140;
if aDatFile '' then begin
AssignFile(f,aDatFile);
Reset(f);
try
while not Eof(F) do begin
Read (F, app);
Cells[0,crow]:= app.Name;
Cells[1,crow]:= app.size;
Cells[2,crow]:= app.Release;
Cells[3,crow]:= app.descript;
Inc(cRow);
RowCount:= crow;
end;
finally
CloseFile(f);
end;
end;// if FileExists...
end; //with
end;
function TBuildAppGrid.GetaDatfile: ShortString;
begin
if FileExists(FaDatFile) then
result:= FaDatFile
else result:= '';
end;
This CLX example of direct access presented so far assumes that only a single user can access the files. If two or more users (or applications) can be permitted to access the data simultaneously, your code must also be build to resolve competition for records.
Next, we have to store the data after editing:
implementation
uses sysutils, QDialogs, QControls,
QStdCtrls;
procedure TBuildAppGrid.SetaDatfile(const Value: ShortString);
begin
if FaDatfile Value then begin
FaDatfile := Value;
end;
end;
procedure TBuildAppGrid.storeGrid;
var
crow: Integer;
begin
//if FModified then
if MessageDlg('Save Changes in ' +
aDatFile, mtConfirmation,mbOkCancel,0) = mrOK then
begin
AssignFile(f, aDatfile);
Rewrite(f);
try
for crow:= 1 to Pred(aGrid.RowCount) do begin
app.Name:= aGrid.Cells[0, crow];
app.size:= aGrid.Cells[1, crow];
app.Release:= aGrid.Cells[2, crow];
app.descript:= aGrid.Cells[3, crow];
Write (f, app);
end;
finally
CloseFile(f);
end;
end; //if MessageDlg...
end;
end.
At least the client calls the class like this:
procedure TForm1.FormCreate(Sender: TObject);
begin
myDatFile:= 'binaries3.txt';
myGridC:= TBuildAppGrid.initGrid(strGrd, myDatFile);
myGridC.fillGrid;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
myGridC.storeGrid;
myGridC.Free;
end;