Teach Yourself Borland Delphi 4 in 21 Days -- Ch 18 -- Building Database Applications
Teach Yourself Borland Delphi 4 in 21 Days
- 18 -
Building Database Applications
Nonvisual Database Programming
Reading from a Database
Creating a Database in Code
Creating the Field Definitions
Using Data Modules
Setting Up a Sample Data Module
Adding to Your Data Module
Running the Data Module
Creating Reports
QuickReport Overview
The QuickRep Component
Report Bands
Creating Reports by Hand
Creating Reports the Easy Way
Deploying a Delphi Database Application
Summary
Q&A
Quiz
Exercises
This chapter is about building database applications. The truth is, I cannot tell
you everything there is to know about writing database applications in one short
chapter. What I can do is point out some issues you are likely to encounter when
writing database applications.
Because you already know how to create database forms, you will spend most of
this day dealing with lower level aspects of database programming. You'll start with
creating and populating a database entirely through code, and then you'll move on
to data modules. Toward the end of the day you'll look at creating database reports
with the QuickReport components. Finally, the day ends with a discussion on deploying
database applications.
Nonvisual Database Programming
Up to this point, you have examined almost exclusively the visual aspects of database
programming with Delphi. That side of database programming is fun and easy, but sooner
or later you have to do some real database programming. The kinds of programs you
have written so far have dealt primarily with simple viewing and editing of data.
In this section, you learn how to perform certain database operations through code.
TIP: I will mention only database operations that pertain to the TTable
class. You could also use SQL to perform database operations in code, but I won't
present that aspect of databases today.
Reading from a Database
This section is short because reading from an existing database isn't difficult.
As an example exercise, you will take the CUSTOMER.DB table and create a comma-delimited
text file from the data. You won't write every field in the table to the file, but
you'll write most of them.
The first step is to create a TTable object. Next, attach it to a database and
to a particular table within the database. Here's how the code looks:
var
Table : TTable;
begin
Table := TTable.Create(Self);
Table.DatabaseName := `DBDEMOS';
Table.TableName := `Customer.db';
end;
This code emulates what Delphi does when you set the DatabaseName and TableName
properties at design time. The next step is to read each record of the database and
write it to a text file. First, I'll show you the basic structure without the actual
code to write the fields to the text file. After that, I'll be more specific. The
basic structure looks like this:
Table.Active := True;
while not Table.Eof do begin
{ Code here to read some fields from the }
{ current record and write them to a text file. }
Table.Next;
end;
Table.Free;
This code is straightforward. First, the table is opened by setting the Active
property to True (you could also have called the Open method). Next, a while loop
reads each record of the table. When a record is written to the text file, the Next
method is called to advance the database cursor to the next record. The while loop's
condition statement checks the Eof property for the end of the table's data and stops
the loop when the end of the table is reached. Finally, the TTable object is freed
after the records have been written to the text file.
NOTE: You don't specifically have to delete the TTable object. When you
create a TTable object in code, you usually pass the form's Self pointer as the owner
of the object--for example,
Table := TTable.Create(Self);
This sets the form as the table's owner. When the form is deleted, VCL will automatically
delete the TTable object. For a main form this happens when the application is closed.
Although you are not required to delete the TTable object, it's always a good idea
to delete the object when you are done with it.
Naturally, you need to extract the information out of each field in order to write
it to the text file. To do that, you use the FieldByName method and the AsString
property of TField. I addressed this briefly on Day 16, "Delphi Database Architecture,"
in the section "Accessing Fields." In the CUSTOMER.DB table, the first
field you want is the CustNo field. Extracting the value of this field would look
like this:
var
S : string;
begin
S := Table.FieldByName(`CustNo').AsString + `,';
Notice that a comma is appended to the end of the string obtained so that this
field's data is separated from the next. You will repeat this code for any fields
for which you want to obtain data. The entire sequence is shown in Listings 18.1
and 18.2. These listings contain a program called MakeText, which can be found with
the book's code. This short program takes the CUSTOMER.DB table and creates a comma-delimited
text file called CUSOMTER.TXT. Listing 18.1 shows the main form's unit (MakeTxtU.pas).
The form contains just a button and a memo. Look over these listings, and then I'll
discuss how the program works.
LISTING 18.1. MakeTxtU.pas.
unit MakeTxtU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ÂDialogs,
StdCtrls, DbTables;
type
TForm1 = class(TForm)
CreateBtn: TButton;
Memo: TMemo;
procedure CreateBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CreateBtnClick(Sender: TObject);
var
Table : TTable;
S : string;
begin
{ Create the Table and assign a database and Table name. }
Table := TTable.Create(Self);
Table.DatabaseName := `DBDEMOS';
Table.TableName := `Customer.db';
{ Change to the busy cursor. }
Screen.Cursor := crHourGlass;
{ We can use a Memo to show the progress as well as to }
{ save the file to disk. First clear the memo of any text.}
Memo.Lines.Clear;
{ Open the Table. }
Table.Active := True;
CreateBtn.Enabled := False;
{ Loop through the records, writing each one to the memo. }
while not Table.Eof do begin
{ Get the first field and add it to the string S, }
{ followed by the delimiter. }
S := Table.FieldByName(`CustNo').AsString + `,';
{ Repeat for all the fields we want. }
S := S + Table.FieldByName(`Company').AsString + `,';
S := S + Table.FieldByName(`Addr1').AsString + `,';
S := S + Table.FieldByName(`Addr2').AsString + `,';
S := S + Table.FieldByName(`City').AsString + `,';
S := S + Table.FieldByName(`State').AsString + `,';
S := S + Table.FieldByName(`Zip').AsString + `,';
S := S + Table.FieldByName(`Phone').AsString + `,';
S := S + Table.FieldByName(`FAX').AsString;
{ Add the string to the Memo. }
Memo.Lines.Add(S);
{ Move to the next record. }
Table.Next;
end;
{ Write the file to disk. }
Memo.Lines.SaveToFile(`customer.txt');
{ Turn the button back on and reset the cursor. }
CreateBtn.Enabled := True;
Screen.Cursor := crDefault;
Table.Free;
end;
end.
All the action takes place in the CreateBtnClick method. When you click the Create File button, the program extracts data from the database table and puts it into the Memo component. First, the value of the CustNo field is read and put into a
string, followed by a comma. After that, each subsequent field's value is appended to the end of the string, again followed by a comma. After all the data has been extracted from a record, the string is added to the memo, using the Add method. When
the end of the table is reached, the memo contents are saved to disk.
I used a Memo component in this example for two reasons. First, by displaying
the results in a memo, you can see what the program produces. Second, the memo component's
Lines property (a TStrings) provides an easy way of saving a text file to disk. Figure
18.1 shows the MakeText program after the file has been written.
FIGURE 18.1. The
MakeText program running.
Creating a Database in Code
Most of what you read on database operations in Delphi tells you how to create
a database with utilities such as Database Desktop. That's fine if your application
has pre-built tables. But if your application has to dynamically create tables, that
approach doesn't work. You must have a way of creating tables through code. For example,
if your application enables users to specify the fields of a table, you won't know
what the fields are until the user has supplied them.
Creating a table in code requires these steps:
1. Create a BDE alias for the database.
2. Create a TTable object.
3. Add field definitions to the FieldDefs property.
4. Add index definitions to the IndexDefs property if your table contains
indexes.
5. Create the actual table with the CreateTable method.
Let's go over these steps individually so that you know what each step involves.
Creating a BDE Alias and a TTable Object
You already performed steps 1 and 2 on Day 16 when I talked about creating a BDE
alias, and you created a TTable object in the preceding section. Here's a recap of
those two steps:
var
Table : TTable;
begin
{ Create the alias. }
CreateDirectory(`c:', nil);
Session.AddStandardAlias(`MyDatabase', `c:', `PARADOX');
Session.SaveConfigFile;
{ Create the table. }
Table := TTable.Create(Self);
Table.DatabaseName := `MyDatabase';
Table.TableName := `MyTable.db';
end;
This code first creates a BDE alias for a database called MyDatabase in the C:
directory. After that, a TTable object is created, and the DatabaseName property
is set to the alias created in the first part of the code. Finally, the TableName
property is set to the name of the new table you are going to create. At this point
the TTable object has been created, but the table itself doesn't exist on disk.
Creating the Field Definitions
The next step is to create the field definitions for the table's fields. As you
might guess, the field definitions describe each field. A field definition contains
the field name, its type, its size (if applicable), and whether the field is a required
field. The field's type can be one of the TFieldType values. Table 18.1 lists the
TFieldType values.
TABLE 18.1. DATABASE TABLE FIELD TYPES.
Field type
Description
ftUnkown
Unknown or undetermined
ftString
Character or string field
ftSmallint
16-bit integer field
ftInteger
32-bit integer field
ftWord
16-bit unsigned integer field
ftBoolean
Boolean field
ftFloat
Floating-point numeric field
ftCurrency
Money field
ftBCD
Binary-Coded Decimal field
ftDate
Date field
ftTime
Time field
ftDateTime
Date and time field
ftBytes
Fixed number of bytes (binary storage)
ftVarBytes
Variable number of bytes (binary storage)
ftAutoInc
Auto-incrementing 32-bit integer counter field
ftBlob
Binary large object field
ftMemo
Text memo field
ftGraphic
Bitmap field
ftFmtMemo
Formatted text memo field
ftParadoxOle
Paradox OLE field
ftDBaseOle
dBASE OLE field
ftTypedBinary Typed binary field
You create a field definition by using the Add method of the TFieldDefs class.
The FieldDefs property of TTable is a TFieldDefs object. Putting all this together,
then, adding a new string field to a database would look like this:
Table.FieldDefs.Add(`Customer', ftString, 30, False);
This code line adds a string field called Customer with a size of 30 characters.
The field is not a required field because the last parameter of the Add method is
False. Add as many field definitions as you need, setting the appropriate data type
and size for each field.
Creating the Index Definitions
If your table will be indexed, you also need to add one or more index definitions.
Adding index definitions is much the same as adding field definitions. The IndexDefs
property of TTable contains the index definitions. To add a new index definition,
call the Add method of TIndexDefs:
Table.IndexDefs.Add(`', `CustNo', [ixPrimary]);
The first parameter of the Add method is used to specify the index name. If you
are creating a primary index (as in this example), you don't need to specify an index
name. The second field is used to specify the field or fields on which to index.
If you have more than one field in the index, you separate each field with a semicolon--for
example,
Table.IndexDefs.Add(`', `Room;Time', [ixPrimary]);
The last parameter of the Add method is used to specify the index type. This parameter
takes a TIndexOptions set. Different index options can be specified in combination.
For example, an index might be a primary index that is sorted in descending order.
In that case, the call to Add might look like this:
Table.IndexDefs.Add(`', `CustNo', [ixPrimary, ixDescending]);
Creating the Table
After you add all the field and index definitions to the table, you create the
table. So far you have been setting up the table's layout but haven't actually created
the table. Creating the table is the easiest step of all:
Table.CreateTable;
The CreateTable method performs the actual creation of the table on disk. CreateTable
takes the contents of the FieldDefs and IndexDefs properties and creates the table
structure, based on those contents.
Now that the table is created, you can fill it with data by using any method required
for your application. You can fill the table programmatically or allow your users
to add or edit the data from a form.
The book's code contains a program called MakeTabl. This program first creates
a table and then fills it with data. The program fills the table with data from the
CUSTOMER.TXT file created with the MakeText program earlier in the chapter. Listing
18.2 contains the unit for the main form (MakeTblU.pas). Note that the uses list
for this program includes the DBGrids, DBTables, and DB units.
LISTING 18.2. MakeTblU.pas.
unit MakeTblU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Grids, DBGrids, DbTables, Db;
type
TForm2 = class(TForm)
DBGrid: TDBGrid;
CreateBtn: TButton;
FillBtn: TButton;
procedure CreateBtnClick(Sender: TObject);
procedure FillBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure TForm2.CreateBtnClick(Sender: TObject);
const
Dir : PChar = `c:';
var
Table : TTable;
begin
{ Create a BDE alias, but only if it doesn't already exist. }
if not Session.IsAlias(`MyDatabase') then begin
CreateDirectory(Dir, nil);
try
Session.AddStandardAlias(`MyDatabase', Dir, `PARADOX');
Session.SaveConfigFile;
except
MessageDlg(`Error Creating Alias', mtError, [mbOk], 0);
Exit;
end;
end;
{ Now create the Table. }
Screen.Cursor := crHourGlass;
Table := TTable.Create(Self);
try
Table.DatabaseName := `MyDatabase';
Table.TableName := `MyTable.db';
{ Add the field definitions for each field. }
Table.FieldDefs.Add(`CustNo', ftFloat, 0, True);
Table.FieldDefs.Add(`Customer', ftString, 30, False);
Table.FieldDefs.Add(`Addr1', ftString, 30, False);
Table.FieldDefs.Add(`Addr2', ftString, 30, False);
Table.FieldDefs.Add(`City', ftString, 15, False);
Table.FieldDefs.Add(`State', ftString, 20, False);
Table.FieldDefs.Add(`Zip', ftString, 10, False);
Table.FieldDefs.Add(`Phone', ftString, 15, False);
Table.FieldDefs.Add(`Fax', ftString, 15, False);
{ Add an index definition for the primary key. }
Table.IndexDefs.Add(`', `CustNo', [ixPrimary]);
{ Everything is set up, so create the Table. }
Table.CreateTable;
except
MessageDlg(`Error Creating Table', mtError, [mbOk], 0);
Screen.Cursor := crDefault;
Table.Free;
Exit;
end;
{ All done, so let the user know. }
Table.Free;
Screen.Cursor := crDefault;
CreateBtn.Enabled := False;
FillBtn.Enabled := True;
MessageDlg(
`Table Created Successfully', mtInformation, [mbOk], 0);
end;
procedure TForm2.FillBtnClick(Sender: TObject);
var
Table : TTable;
Datasource : TDataSource;
Lines : TStringList;
S, S2 : string;
I, P : Integer;
begin
{ Create a TTable. }
Table := TTable.Create(Self);
Table.DatabaseName := `MyDatabase';
Table.TableName := `MyTable.db';
{ Create a data source and hook it up to the Table. }
{ Then hook the DBGrid to the datasource. }
Datasource := TDataSource.Create(Self);
Datasource.DataSet := Table;
DBGrid.Datasource := Datasource;
{ Open the Table and the text file. }
Table.Active := True;
Lines := TStringList.Create;
Lines.LoadFromFile(`customer.txt');
{ Put the Table in edit mode. }
Table.Edit;
{ Process the Lines. }
for I := 0 to Pred(Lines.Count) do begin
{ Append a record to the end of the file. }
Table.Append;
{ Parse the string and get the first value. }
S := Lines[I];
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
{ Write the value to the CustNo field. }
Table.FieldByName(`CustNo').Value := StrToInt(S2);
{ Continue for each of the fields. }
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`Customer').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`Addr1').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`Addr2').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`City').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`State').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`Zip').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`Phone').Value := S2;
P := Pos(`,', S);
S2 := Copy(S, 1, P - 1);
Delete(S, 1, P);
Table.FieldByName(`FAX').Value := S2;
{ We might get a key violation exception if we try to add a }
{ record with a duplicate customer number. If that happens, }
{ we need to inform the user, cancel the edits, put the }
{ put the Table back in edit mode, and continue processing. }
try
Table.Post;
except
on EDBEngineError do begin
MessageBox(Handle,
`Duplicate Customer Number', `Key Violation', 0);
Table.Cancel;
Table.Edit;
Continue;
end;
end;
end;
{ All done with the TStringList so it. }
Lines.Free;
{ We won't the Table so that the Table's data shows }
{ in the DBGrid. VCL will the Table and Datasource }
{ for us. }
{Table.Free; }
{Datasource.Free; }
end;
end.
Using Data Modules
As you know by now, setting up database components is easy in Delphi. Still, there
is some time involved, even for the simple examples you have been writing.
You have to place a Table or Query component on the form and choose the database
name. Then you have to set the table name (for a Table) or SQL property (for a Query).
You might have to set other properties, depending on how you are using the database.
You might also have several events to handle. Next, you have to place a DataSource
component on the form and attach it to the table or query. If your application makes
use of a Database component, you have to set that component's properties and events
as well. None of this is hard, but it would be nice if you could do it all just once
for all your programs. Data modules enable you to do exactly that.
At the base level, data modules are really just specialized forms. To create a
data module, open the Object Repository and double-click the Data Module icon. Delphi
creates the data module and a corresponding source unit just as it does when you
create a new form. You can set the Name property of the data module and save it to
disk, again, just like a form.
After you create the data module, you place data access components on it. Then
you set all the properties for the data access components as needed. You can even
create event handlers for any components on the data module. When you have set everything
just the way you want it, save the data module. Every form in your application can
then access the data module.
Setting Up a Sample Data Module
A simple exercise will help you understand data modules better. First, you'll
set up the data module, and then you'll put it to work:
1. Create a new application. Change the main form's Name property to MainForm.
Save the project. Save the main form as DSExMain.pas and the project as DSExampl.dpr.
2. Choose File|New. When the Object Repository appears, double-click the
Data Module icon to create a new data module. The Form Designer displays the data
module. Change the Name property to DBDemos.
3. Click on the Data Access tab on the Component palette. Place a Table
component on the data module. Change the DatabaseName property to DBDEMOS and the
TableName to ANIMALS.DBF. Change the Name property to AnimalsTable.
4. Place a second Table component on the data module. Set the DatabaseName
property to DBDEMOS and the TableName property to BIOLIFE.DB. Change the Name property
to BiolifeTable.
5. Place a DataSource component on the data module. Change its Name property
to Animals and its DataSet property to AnimalsTable.
6. Place another DataSource component on the data module. Change this
data source's Name to Biolife and its DataSet property to BiolifeTable. Your data
module now looks like Figure 18.2.
FIGURE 18.2. The
finished data module.
7. Double-click on the background of the data module. An OnCreate event
handler will be created for the data module. Type this code in the event handler:
AnimalsTable.Open;
BiolifeTable.Open;
8. Save the project. When prompted, save the data module as DataMod.pas.
Adding to Your Data Module
Now let's put the new data module to use. You are going to create two buttons
for the application's main form. One button will display a form that shows the Animals
table, and the other will display the Biolife table. Here goes:
1. Create a new form. Change the form's Caption property to Animals Form
and the Name property to AnimalsForm.
2. Choose File|Use Unit. Choose the DataMod unit from the Use Unit dialog
and click OK. The data module is now accessible from the main form.
3. Place a DBGrid component and a DBNavigator component on the form. Select
both the DBGrid and DBNavigator components. Locate the DataSource property in the
Object Inspector and click the drop-down arrow next to the property. You will see
the following displayed in the list of available data sources:
DBDemos.Animals
DBDemos.Biolife
Choose the DBDemos.Animals data source.
4. Save the unit as DSExU2.pas (or a more meaningful name if you like).
5. Again, create a new form for the project. Repeat steps 1 through 3,
but this time choose DBDEMOS.Biolife for the data source and change the Caption to
BioLife Form. Change the Name property to BiolifeForm. Save the form as DSExU3.pas.
Figure 18.3 shows the Delphi IDE after completing this step.
Running the Data Module
These steps demonstrate that after you create a data module, you can use the components
on that data module from anywhere in your program. All you have to do is use the
unit for the data module, and then all data-aware components will be capable of detecting
the data module. Let's finish this application so that you can try it out:
1. Place a button on the main form. Change its Caption property to Show
Animals.
2. Double-click the button to generate an OnClick handler for the button.
Type this code in the event handler:
AnimalsForm.Show;
3. Drop another button on the form. Change its Caption property to Show
Biolife.
4. Create an OnClick event handler for this button and type the following
code in the event handler:
BiolifeForm.Show;
5. Choose File|Use Unit. Choose the DSExU2 unit and click OK.
6. Repeat step 5 to include the DSExU3 unit.
Now you can run the program. When you click a button, the appropriate form will
appear. Figure 18.4 shows the program running.
FIGURE 18.3. The second
form completed.
FIGURE 18.4. The
program running with both forms displayed.
Data modules make it easy to set up your database components once and then reuse
those components over and over. After you create a data module, you can save it to
the Object Repository, where it is always available for your use.
Creating Reports
A database program is not complete without some way of viewing and printing data,
and that is where reports enter the picture. Up to this point, you have been looking
at ways to view individual records or multiple records with the DBGrid component.
These methods might be perfect for some applications, but sooner or later you will
need more control over the viewing of records.
Besides viewing the data onscreen, you will almost certainly need to print the
data. A database application that can't print is not very useful. Delphi's QuickReport
components enable you to view and print your data with ease.
QuickReport Overview
Before you can create a report, you need an overview of how the QuickReport components
work.
The QuickRep Component
The base QuickReport component is the QuickRep component. This component acts
as a canvas on which you place the elements that your report will display (I'll discuss
the report elements soon).
The QuickRep component has properties that affect the way the report will appear
when printed. For example, the Page property is a class containing properties called
TopMargin, BottomMargin, LeftMargin, RightMargin, Columns, Orientation, PaperSize,
and so on.
The PrinterSettings property is also a class. This property has its own properties,
called Copies, Duplex, FirstPage, LastPage, and OutputBin. The ReportTitle property
is used to display the print job description in the Windows Print Manager and in
the title bar of the QuickReport preview window. The Units property controls whether
margins are displayed in millimeters, inches, picas, or other choices. The DataSet
property is used to set the dataset from which the report's data will be obtained.
NOTE: The DataSet property must be set and the associated dataset must
be active before anything will show up in the report.
Primary QuickRep methods include Preview and Print. The Print method, as its name
implies, prints the report. The Preview method displays a modal preview window. The
preview window includes buttons for viewing options, first page, last page, previous
page, next page, print, print setup, save report, open report, and close preview.
Figure 18.5 shows the QuickReport preview window at runtime.
FIGURE 18.5. The
QuickReport preview window.
QuickRep events of note include OnPreview and OnNeedData. You can use the OnPreview
event, instead of the default preview window, to provide a custom preview window.
When using a data source other than a VCL database, you use the OnNeedData event.
For example, you can create a report from a string list, an array, or a text file.
Report Bands
A QuickReport is composed of bands. Bands come in many different types.
A basic report has at least three types: a title band, a column header band, and
a detail band. The title band contains the report title, which is displayed only
on the first page of the report. The column header band is used to display the column
headers for the fields in the dataset; the column header appears at the top of every
page. Some reports, such as a report used to generate mailing labels, do not have
a column headers band.
The detail band is the band that does all the work. On the detail band you place
any data that you want in the report. You define the contents of the detail band,
and QuickReport repeats the detail band for every record in the dataset. In a minute
you'll do an exercise that illustrates how the different bands work.
Other commonly used band types include page header, page footer, group header,
group footer, and summary bands. The QRBand component defines a QuickReport band.
The BandType property is used to specify the band type (title, detail, header, footer,
and so on).
The bands automatically arrange themselves on the QuickRep component, based on
the band's type. For example, if you place a QRBand on the report and change its
BandType to rbPageFooter, the band will be moved below all other bands. Likewise,
a page header band will be placed above all other bands.
QuickReport Design Elements
QuickReport design elements come in three forms. The first form includes components
for text labels, images, shapes, headers, footers, and so on. These components are
primarily used to display static design elements. For example, the report title is
usually set once and then doesn't change. Another example is a graphic used to display
a company's logo on the report. The components in this group are very similar to
the standard VCL components. The QRLabel component resembles a standard Label component,
a QRImage is similar to the VCL Image component, the QRShape component is similar
to the regular Shape component, and so on. Use these components to design the static
portions of your reports.
The second category of elements contains QuickReport versions of the standard
VCL data-aware components. These components are placed on the detail band of a report.
Components in this group include the QRDBText, QRDBRichEdit, and QRDBImage components.
Data is pulled from the dataset and placed into these components to fill the body
of the report.
The third group of QuickReport components includes the QRSysData component and
the QRExpr component. The QRSysData component is used to display page numbers, the
report date, the report time, the report title, and so on. The QRExpr component is
used to display calculated results. The Expression property defines the expression
for the calculation. The Expression property has a property editor called the Expression
builder that is used to define simple expressions. An expression might be simple,
such as multiplying two fields, or complete with formulas such as AVERAGE, COUNT,
or SUM.
Creating Reports by Hand
Certainly the best way to write truly custom reports is by hand. That might sound
difficult, but fortunately the QuickReport components make it easy. The best way
for me to explain how to create reports by hand is to take you through an exercise.
This exercise creates a simple application that displays and prints a report in list
form. I won't tell you to perform every single step in this exercise. For example,
I won't tell you to save the project or give you filenames to use--you can figure
those out for yourself. Also, you don't have to worry about making the report real
pretty at this point. You can go back later and tidy up.
The first step is to create the main form of the application. After that's done,
you create the basic outline of the report. Here goes:
1. Create a new application. Place two buttons on the main form. Change
the caption of the first button to Preview Report and the caption of the second button
to Print Report.
2. Choose File|New. Double-click the Report icon in the Object Repository.
Delphi creates a new QuickReport form.
3. Place a Table component on the QuickReport form. Change the DatabaseName
property to DBDEMOS and the TableName property to EMPLOYEE.DB. Set the Active property
to True.
4. Select the QuickReport form. Change the DataSet property to Table1
and change the ReportTitle property to Employee Report.
5. Switch back to the main form. Double-click the button labeled Preview
Report. Type this code in the OnClick event handler:
QuickReport2.Preview;
6. Double-click the Print Report button and type the following in the
OnClick event handler:
QuickReport2.Print;
7. Choose File|Use Unit and include the QuickReport form.
Now you have a blank report. What you need to do next is add a title band, a column
header band, and a detail band. For the next few steps you might want to look ahead
to Figure 18.6 to see the final result. Follow these steps:
1. Select a QRBand component from the QReport tab of the Component palette
and place it on the report. The band is a title band by default.
2. Select a QRLabel component and place it on the title band. Change the
Caption property to Employee Report and change the Font property to your preference
(I used Arial, 18 point, bold). Align the component so that it is centered on the
band.
3. Place another band on the report. Change the BandType property to rbColumnHeader
and change the Font to bold and underlined.
4. Place a QRLabel on the left side of the column header band and change
the Caption property to Employee Number. Place a second QRLabel on the band to the
right of the first and change the Caption property to Name. Place a third QRLabel
on the band to the right of the last label and change the Caption to Salary.
5. Place another band on the report and change the BandType property to
rbDetail. Notice that the band moves below the other bands after you change the band
type.
6. Place a QRDBText component on the left edge of the detail band (align
it with the Employee Number label on the column header band). Change the DataSet
property to Table1 and the DataField property to EmpNo.
7. Place another QRDBText on the detail band and align it with the Name
component on the column header band. Change the DataSet property to Table1 and the
DataField property to FirstName. Place another QRDBText just to the right of the
last one (see Figure 18.6). Attach it to the LastName field of the database table.
8. Add a final QRDBText component on the detail band. Place it below the Salary component on the column header band and attach it to the Salary field in the table. Your form now looks like Figure 18.6.
FIGURE 18.6. Your
QuickReport form.
You are probably wondering what the report will look like on paper. Guess what?
You don't have to wait to find out. Just right-click on the QuickReport form and
choose Preview from the context menu. The QuickReport preview window is displayed,
and you can preview the report.
To print the report, click the Print button. When you are done with the report
preview, click the Close button. You can now run the program and try out the Preview
Report and Print Report buttons.
NOTE: The report you just created is double-spaced. To change the spacing,
reduce the height of the detail band.
Before leaving this discussion of reports, let me show you one other nice feature
of QuickReport. Right-click on the QuickReport form and choose Report settings from
the context menu. The Report Settings dialog will be displayed, as shown in Figure
18.7. This dialog enables you to set the primary properties of the QuickRep component
visually rather than use the Object Inspector.
FIGURE 18.7. The
QuickReport Report Settings dialog.
Creating Reports the Easy Way
Delphi comes with three built-in QuickReport forms, which can be found on the
Forms tab of the Object Repository. The forms are called Quick Report Labels, Quick
Report List, and Quick Report Master/Detail. These forms give you a head start on
creating reports. You can generate one of the forms from the Object Repository and
then modify the form as needed.
Deploying a Delphi Database Application
As I said on Day 16, the Borland Database Engine (BDE) is a collection of DLLs
and drivers that enable your Delphi application to talk to various types of databases.
When you ship an application that uses the BDE, you need to make sure that you ship
the proper BDE files and that they are properly registered on your users' machines.
The most sensible way to do this is with a Borland-approved installation program.
Here at TurboPower Software, we use the Wise Install System from Great Lakes Business
Solutions (http://www.glbs.com). Others include
InstallShield and its little brother, InstallShield Express. Conveniently, InstallShield
Express comes with Delphi Professional and Client/Server, so if you have one of those
versions of Delphi, you don't have to rush right out and buy an installation program.
You might be wondering why Borland is involved in dictating how the BDE must be
deployed. The reason is simple when you think about it: There are many BDE versions
in use. Some folks might be writing and deploying applications that use the BDE from
Delphi 1. Others might be using the BDE from C++Builder 1. Still others could be
using the BDE that comes with Delphi 4 in their applications.
The important point to realize is that the BDE is backward compatible. Newer BDE
versions are guaranteed to work with applications written for older BDE versions.
This system will only work, however, as long as everyone plays by the rules. Part
of the rules say, "Thou shalt not arbitrarily overwrite existing BDE files."
Certified installation programs check the version number of any BDE files they find.
If the file being installed is older than the existing file, the installation program
leaves the existing file in place.
This ensures that the user will always have the latest BDE files on his or her
system. Another service that these certified installation programs provide is to
determine exactly which files you need to deploy for your application. You should
read DEPLOY.TXT in the Delphi root directory for more details on deploying applications
that use the BDE.
Summary
There's no question that creating database applications requires a lot of work.
The good news is that Delphi makes the job much easier than other development environments.
Today you found out something about the nonvisual aspects of database programming.
You also found out about data modules and how to use them. You ended the day with
a look at QuickReport. QuickReport makes creating reports for your database applications
easy. I also explained what is required to deploy a database application.
Workshop
The Workshop contains quiz questions to help you solidify your understanding of
the material covered and exercises to provide you with experience in using what you
have learned. You can find answers to the quiz questions in Appendix A, "Answers
to the Quiz Questions."
Q&A
Q I am trying to create a database at runtime. I have created a BDE alias
and set all the field definitions, but the table is never created on my hard disk.
What have I done wrong?
A You have probably failed to call the CreateTable method. You must call
this method to physically create the table.
Q When designing a report, can I set all my report's properties at one time?
A Yes. Just right-click the QuickRep component and choose Report settings
from the context menu. The Report Settings dialog is displayed, and you can set most
of the report properties visually.
Q Can a data module include code as well as components?
A Yes. A data module can contain any code necessary to carry out operation
of the data module. The data module can contain event handlers or methods that you
create. The methods you create can be public or private (for the data module's use
only).
Q When I preview my report, it is blank. What is wrong?
A More than likely you have not set the Active property of the dataset
to True. The dataset must be open before the report will function.
Q Can I use more than one detail band on a report?
A No. You can place more than one detail band on a report, but only the
first one will be used when the report is generated.
Q Why do I need a Borland-approved installation program to install my database
application?
A The BDE is complicated to install, and an approved installation program
is guaranteed to do the installation correctly.
Quiz
1. What method do you call to create a database table at runtime?
2. What does the Edit method of TTable do?
3. What method do you call when you want to apply the changes made to
a record?
4. How do you create a new data module?
5. Is a data module a regular form?
6. What method do you call to print a QuickReport?
7. What type of QuickReport band displays the dataset's data?
8. What component is used to display the page number on a report?
9. How can you preview a report at design time?
10. What is the QRExpr component used for?
Exercises
1. Create a database (a BDE alias) and a table for the database, either
through code or by using the Delphi database tools.
2. Create a data module containing the database table from exercise 1.
3. Generate a report that creates mailing labels. (Hint: Start with a
QuickReport Labels object from the Forms page of the Object Repository.)
4. Modify the QuickReport you created in this chapter so that the employee's
first and last names are displayed by a QRExpr component.
5. Read the DEPLOY.TXT file in your Delphi directory to understand what
is involved in deploying a Delphi database application.
© Copyright, Macmillan Computer Publishing. All
rights reserved.
Wyszukiwarka
Podobne podstrony:
ch18Scenariusz 16 Rowerem do szkołyr 1 nr 16 138669446416 narrator16 MISJAFakty nieznane , bo niebyłe Nasz Dziennik, 2011 03 16990904 1616 (27)więcej podobnych podstron