Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
2004
Volume 10 Number 7
INSIDE
ON THE COVER
Delphi at Work
Excel to PDF
Excel PDF
to
FEATURES
What Is
ASP.NET Really?
Briefcase Apps
Delphi Informant
www.DelphiZine.com
Made Easy
Smarter
ADO Connections
More Everyday Tasks
with ADO.NET
Everyday Delphi
12
In Development
16
Active Delphi
19
ASP.NET: Part I
REVIEWS
22 Shell+
24
1
D E PA R T M E N T S
26 File | New by Alan C. Moore, Ph.D.
D E L P H I
AUTOMATION
A T
W O R K
By Mark Meyer
Excel to PDF
Conversion Made Easy through Automation
Easy to Use
TExcel2PDF is a wrapper around the TExcelApplication
component. In Delphi 7 Enterprise Edition, the
TExcelApplication component is located on the Servers tab.
TExcel2PDF makes short work of printing Excel documents
to PDF by allowing the user to set a few simple properties
and then fire one small public method: Print. Generating
PDF documents from Excel spreadsheets is as simple as
executing the following code snippet:
Excel2PDF1.InputDirectory :=
Extractfilepath(PTShellList1.ShListData[i].PathName);
Excel2PDF1.InputFilename :=
PTShellList1.ShListData[i].FileName;
Excel2PDF1.Print;
When you review the code for TExcel2PDF youll find that
the workhorse method of TExcel2PDF is a private method
named _Print_Special_ (shown in Figure 3). Like most wrapper components, TExcel2PDF provides a simple, easy-to-use
interface to a few Windows Automation APIs exposed by the
underlying TExcelApplication component.
(More Than) A Few Words about PDF
Printer Setup
As I stated before, using TExcel2PDF is simple. Setting up
your PDF printer is slightly more involved, but necessary
in order to use our tool. Dont worry though; well go
through the process step-by-step.
The first thing you need to do is install Adobe Acrobat
on your system. Adobes marketing nomenclature and
product array is somewhat daunting to the uninitiated.
Essentially you will need access to the Adobe Acrobat
5 or 6 client-based Full Install. When the install first
Delphi
at
Work
Excel to PDF
TExcel2PDF = class(TComponent)
private
FExcelApp : TExcelApplication;
FLCID, FCopies : Integer;
FOutputDirectory, FOutputFilename, FInputFilename,
FInputDirectory, FPDFPrinter : string;
FPrinttofile, FUpdateLinks, FAutoConnect, FAutoQuit,
FExcelVisible, FOpenReadOnly, FEditable,
FPromptForFilename : Boolean;
protected
function GetAutoConnect: Boolean;
function GetAutoQuit: Boolean;
function GetExcelVisible: Boolean;
procedure SetAutoConnect(Value: Boolean);
procedure SetAutoQuit(Value: Boolean);
procedure SetExcelVisible(Value: Boolean);
procedure _Print_Special_;
procedure _Open_;
procedure _Close_;
public
procedure Print;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property LCID: Integer read FLCID;
property PDFPrinter: string
read FPDFPrinter write FPDFPrinter;
property OutputFilename: string
read FOutputFilename write FOutputFilename;
property OutputDirectory: string
read FOutputDirectory write FOutputDirectory;
property InputFilename: string
read FInputFilename write FInputFilename;
property InputDirectory: string
read FInputDirectory write FInputDirectory;
property PrintToFile: Boolean
read FPrintToFile write FPrintToFile;
property Copies: Integer read FCopies write FCopies;
property UpdateLinks: Boolean
read FUpdateLinks write FUpdateLinks;
property AutoConnect: Boolean
read GetAutoConnect write SetAutoConnect;
property AutoQuit: Boolean
read GetAutoQuit write SetAutoQuit;
property ExcelVisible: Boolean
read GetExcelVisible write SetExcelVisible;
property OpenReadOnly: Boolean
read FOpenReadOnly write FOpenReadOnly;
property Editable: Boolean
read FEditable write FEditable;
property PromptForFilename: Boolean
read FPromptForFilename write FPromptForFilename;
end;
procedure TExcel2PDF._Print_Special_;
var
i : Integer;
begin
try
try
Self._Open_;
if PromptForFilename then
FExcelApp.ActiveWorkbook.PrintOut(EmptyParam,
EmptyParam, EmptyParam, EmptyParam, PDFPrinter,
True, EmptyParam, EmptyParam, LCID);
else if (Copies > 1) then
for i := 1 to Copies do
FExcelApp.ActiveWorkbook.PrintOut(
EmptyParam, EmptyParam, EmptyParam, EmptyParam,
PDFPrinter, True, EmptyParam, OutputDirectory +
'\' + OutputFilename + '_' + inttostr(i) +
'_' + POST_SCRIPT_EXT, LCID);
else
FExcelApp.ActiveWorkbook.PrintOut(EmptyParam,
EmptyParam, EmptyParam, EmptyParam, PDFPrinter,
PrintToFile, EmptyParam, OutputDirectory + '\' +
OutputFilename + POST_SCRIPT_EXT, LCID);
finally
Self._Close_;
end;
except
on EOleException do
// Handle if needed.
end;
end;
Delphi
at
Work
Excel to PDF
have made your selection, hit the Print to PDF button, and
your PDF files will be generated. As evidence that the
process is working you can check your Windows toolbar
for the distinctive Adobe trademark.
Okay; time for a pop quiz: Where do your PDF files end
up on your hard drive? If you answered, They go to
the location that I defined when I set up my PDF printer
port, you are correct! In the example above, I chose to set
up a new PDF Port associated with C:\PDFPrinterOutput
(see Figure 7).
The demo application is straightforward, but a few of its
options merit additional explanation. These are Print To File,
Prompt For Filename, and Copies. Ill cover each of them in
turn below.
Delphi
at
Work
Excel to PDF
watched folder and change them to
PDF documents.
Figure 7: Windows directory associated with PDF port containing generated PDF files.
Figure 9: Files generated with the Copies option showing numeric identifier appended to each filename.
References
Debra Pates Web site: www.djpate.freeserve.co.uk/
Automation.htm
Adobes Web site: www.adobe.com/products/acrobat/
main.html
Plasmatech Web site: www.plasmatech.com/shellctl.htm
The source and demo app referenced in this article are available
for download on the Delphi Informant Magazine Complete
Works CD located in INFORM\2004\JUL\DI200407MM.
C O L U M N S
ADO.NET
&
R O W S
By Bill Todd
Columns
&
Rows
procedure TCountryForm.OpenCountry;
var
CountryPrimaryKey: array of DataColumn;
begin
// Set name of Table that Fill will create to Country.
CountryAdapter.TableMappings.Add('Table1', 'Country');
CountryAdapter.Fill(EmpDataSet, 'Country');
// Create dynamic array of DataColumn objects, add
// Country column to the array, and set the PrimaryKey
// property of the DataTable.
SetLength(CountryPrimaryKey, 1);
CountryPrimaryKey[0] :=
EmpDataSet.Tables['Country'].Columns['Country'];
EmpDataSet.Tables['Country'].PrimaryKey :=
CountryPrimaryKey;
// Bind the DataGrid to the DataTable.
CountryGrid.DataSource :=
EmpDataSet.Tables['Country'].DefaultView;
end;
procedure TCountryForm.MergeItem_Click(
Sender: System.Object; e: System.EventArgs);
var
TempTable: DataTable;
R: DataRow;
begin
// If the Country table does not exist in EmpDataSet,
// create and fill it.
if (EmpDataSet.Tables['Country'] = nil) then
OpenCountry;
// Create a new table by cloning the Country table.
TempTable := EmpDataSet.Tables['Country'].Clone;
// Add a new column.
TempTable.Columns.Add('Continent', TypeOf(string));
// Add two rows.
R := TempTable.NewRow;
R['Country'] := 'China';
R['Currency'] := 'Yuan';
R['Continent'] := 'Asia';
TempTable.Rows.Add(R);
R := TempTable.NewRow;
R['Country'] := 'USA';
R['Currency'] := 'Dollar';
R['Continent'] := 'North America';
TempTable.Rows.Add(R);
// Merge TempTable into the Country table in EmpDataSet.
EmpDataSet.Merge(TempTable, False,
MissingSchemaAction.Add);
end;
Figure 3 shows the code from the Click event handler of the
Demo | Merge menu choice. This method starts by checking
to see if the Country table exists in the DataSet named
EmpDataSet. If the DataTable doesnt exist, OpenCountry
is called to fill the DataSet and bind the DataGrid to the
table. Next, a new DataTable, named TempTable, with the
same structure as the Country table, is created by calling
Columns
&
Rows
procedure TCountryForm.CountDollarItem_Click(
Sender: System.Object; e: System.EventArgs);
var
DollarCount: Integer;
CountryTbl: DataTable;
begin
// If the Country table does not exist in EmpDataSet,
// create and fill it.
if (EmpDataSet.Tables['Country'] = nil) then
OpenCountry;
CountryTbl := EmpDataSet.Tables['Country'];
DollarCount := Integer(CountryTbl.Compute(
'Count(Country)', 'Currency LIKE ''%Dollar'''));
MessageBox.Show('There are ' + DollarCount.ToString +
' countries using dollars.');
end;
Figure 4: The Click event handler for the Count Countries Using Dollars
menu choice.
procedure TCountryForm.LoadDataRowItem_Click(
Sender: System.Object; e: System.EventArgs);
var
TempTable: DataTable;
R: DataRow;
begin
// If the Country table does not exist in EmpDataSet,
// create and fill it.
if (EmpDataSet.Tables['Country'] = nil) then
OpenCountry;
// Create a new table by cloning the Country table.
TempTable := EmpDataSet.Tables['Country'].Clone;
// Add Two rows.
TempTable.LoadDataRow(['China', 'Yuan'], False);
TempTable.LoadDataRow(['Russia', 'Ruble'], False);
// Merge TempTable into the Country table in EmpDataSet.
EmpDataSet.Merge(TempTable, False,
MissingSchemaAction.Add);
end;
Columns
&
Rows
Figure 6: The Click event handler for the ImportRow menu item.
9
Figure 7: The Click event handler of the ImportRow Fails menu item.
procedure TCountryForm.ByCurrencyItem_Click(
Sender: System.Object; e: System.EventArgs);
begin
EmpDataSet.Tables['Country'].DefaultView.Sort :=
'Currency DESC, Country';
end;
Columns
&
Rows
DataViewRowState Member
Includes
Added
CurrentRows
Deleted
Deleted rows.
ModifiedCurrent
ModifiedOriginal
None
OriginalRows
Unchanged
Unmodified rows.
procedure TCountryForm.ChangeRowsItem_Click(
Sender: System.Object; e: System.EventArgs);
begin
EmpDataSet.Tables['Country'].DefaultView.RowStateFilter:=
DataViewRowState.Added or
DataViewRowState.ModifiedCurrent;
end;
Figure 10: The Changed Rows menu items Click event handler.
You can see that these statements are not the same, by using
the first statement to bind a DataGrid directly to a DataTable,
then setting the default DataViews Sort property. The order
of the rows in the DataGrid will not change unless you rebind
the DataGrid to the DataTable by reassigning the DataTable
to the DataGrid.DataSource property. However, as the sample
application demonstrates, if you bind the DataGrid to the
default DataView and set the DefaultView.Sort property, the
order of the data in the DataGrid changes.
You can filter the DataView using any combination of the
members of the DataViewRowState enumeration by setting
the DataView.RowStateFilter property. Figure 9 shows
the members of the DataViewRowState enumeration, and
explains which rows will be shown if that member is
included in the filter.
You can or members of the enumeration together to display any combination of rows. Figure 10 shows the code
from the DataView | Row State Filter | Changed Rows menu item.
This code displays only the new or modified rows, and the
modified rows are shown with their current values.
10
procedure TCountryForm.FilterItem_Click(
Sender: System.Object; e: System.EventArgs);
begin
EmpDataSet.Tables['Country'].DefaultView.RowFilter :=
'Currency LIKE ''%Dollar''';
end;
procedure TCountryForm.EditRowItem_Click(
Sender: System.Object; e: System.EventArgs);
var
Drv: DataRowView;
begin
Drv := EmpDataSet.Tables['Country'].DefaultView[2];
Drv.BeginEdit;
Drv['Currency'] := 'Unknown;
Drv.EndEdit;
end;
procedure TCountryForm.DeleteRowItem_Click(
Sender: System.Object; e: System.EventArgs);
var
Drv: DataRowView;
begin
Drv := EmpDataSet.Tables['Country'].DefaultView[2];
Drv.Delete;
end;
procedure TCountryForm.AddRowItem_Click(
Sender: System.Object; e: System.EventArgs);
var
Drv: DataRowView;
begin
Drv := EmpDataSet.Tables['Country'].DefaultView.AddNew;
Drv['Country'] := 'China';
Drv['Currency'] := 'Yuan';
Drv.EndEdit;
end;
Columns
&
Rows
procedure TCountryForm.FindItem_Click(
Sender: System.Object; e: System.EventArgs);
var
RowIndex: Integer;
Dv: DataView;
OldSort: string;
begin
// Get a reference to default DataView to save typing.
Dv := EmpDataSet.Tables['Country'].DefaultView;
// Save the current sort order.
OldSort := Dv.Sort;
// Sort by Currency and find row with Currency = FFranc.
Dv.Sort := 'Currency';
RowIndex := Dv.Find('FFranc');
// If a matching row was found...
if (RowIndex <> -1) then // Display name of country.
MessageBox.Show(Dv[RowIndex][Country'].ToString +
' uses the FFranc.');
// Return to the original sort order.
Dv.Sort := OldSort;
end;
E V E R Y D A Y
BRIEFCASE APPS
SQL SERVER
D E L P H I
DELPHI 5-8
By Rick Spence
12
Everyday
Delphi
CompanyName:Alfreds Futterkiste
ContactName:Maria Anders
Everyday
Delphi
14
Everyday
Delphi
Conclusion
In this article I demonstrated how to use ClientDataSets
to implement briefcase applications. You must use
the DataSetProvider property to initially load the
ClientDataSet from an actual database table, and to
apply the changes back to the server. Make sure you
set the providers UpdateMode property to tell it how
to locate records on the server (and therefore in what
circumstances to report errors), and dont forget to set
the ProviderFlags properties of the persistent fields in the
dataset to indicate which fields form the primary key.
Finally, youll find it easiest to use the Borland-supplied
Reconcile Error dialog box to report errors to the user.
15
I N
ADO
D E V E L O P M E N T
DATABASE CONNECTIVITY
DELPHI 4-8
By Bob Fleischman
And aside from the obvious, what exactly was your problem? Popi again inquired.
In
Development
In
Development
You tell me, Popi challenged. When do you need the connection string?
Well, I suppose when I want to connect, I told him matter-of-factly. But ADO has a Connected property a Boolean, I believe. So we can just hook into the BeforeConnect
event and build our string then.
That we could, Popi responded with a wry smile. But
why not go closer to the source. Lets just do this:
property Connected: Boolean
read GetConnected write SetConnected;
And just how did you know the connection string variants
to write out? I asked, impressed as always by Popis vast
store of knowledge.
Well, actually, it was quite easy, he said. I simply used
the Connection String wizard to create a few variants
18
A C T I V E
ASP.NET
D E L P H I
DELPHI 8
By Nick Hodges
ASP.NET
ASP.NET is Microsofts Web application development framework that is a core part of the .NET Framework, delivering
a robust architecture for developing Web server-based applications that provide their user interface via HTML and a
Web browser. The architecture provides literally hundreds of
classes and types for you to use to build and manage your
Web sites and Web applications. In addition, the architecture
is quite extensible, allowing you to build or buy components
to enhance your ability to develop cool, dynamic, and powerful Web applications.
Before I go much further, I have a confession to make.
I didnt really like ASP.NET when it first came out. It
seemed so unwieldy with all those pages and files. Each
page appeared to be sort of its own little program. The
script mixed in with the HTML made me cringe, because
the user interface elements were all mashed in with the
code logic. Plus, WebSnap was so cool and powerful that
my bigoted brain couldnt imagine why anyone would
want to use ASP.NET.
Element
Description
Example
The code-behind binary file. It contains all the code from your *.pas
files thats associated with all the pages, as well as the global object
for your application. Your *.aspx pages will reference this file to find the
code they need to make your application work.
MyWebApp.dll
Pages containing the HTML and ASP.NET control tags that will make up
the user interface of your application. These pages are normally created in the WebForms designer, although they can be hand coded and
edited using any appropriate tool.
MainPage.aspx,
LoginPage.aspx,
MenuColumn.ascx,
etc.
web.config file
web.config
To be deployed, your application must reside in an IIS virtual directory. When you create a new ASP.NET application in the IDE, you can
choose to have it create the directory for you, or you can always create it later yourself.
C:\Inetpub\wwwroot\
WebApplication1
19
Active
Delphi
ASP.NET
Filename
Description
Filename
Description
Global.asax
System.Web
Global.pas
Web.config
Webform1.aspx
Webform1.pas
System.Web.UI,
Bread-and-butter namespaces that
SystemWeb.UI.HTMLControls, hold the classes that will make up
System.Web.UI.Controls
the majority of the functionality in
your Web applications. They hold
the classes for managing pages, the
HTML, and controls on those pages.
Put together, these three namespaces
make up what is commonly called
WebForms.
System.Web.SessionState
20
Active
Delphi
ASP.NET
will do most of that work for you, but you can if you need
or want to. The HTTPRequest class is filled out with the
information from each request that comes in, and either
Aspnet_isapi.dll is really just a scripting engine that hosts
you or the framework will fill out the HTTPResponse
the .NET Framework, and knows how to take aspx pages
class with the response you want sent to the client. The
and use the pages associated DLL to render HTML pages
HTTPContext class also provides access to application
for IIS. Cool, eh? I bet most people didnt know that
services like the Session object, the Application object, and
their ASP.NET application was really just another Win32
information about the current user. The Page object will
scripting language as far as IIS is concerned.
do almost all of the HTTP handling in a typical ASP.NET
application, but the framework provides a robust set
So, what is ASP.NET on the project side? Have a look
of classes that allow you to provide any type of HTTP
for yourself. Fire up
handling for a request such
Delphi 8, select File | New
as image handling or other
Any Web application development types of binary responses.
| ASP.NET Web Application,
framework is really an elaborate way
and have a look at
Ill cover this type of
what the resulting
specialized HTTP handling
to accept HTTP requests, process
project includes. First,
those requests, and provide an HTTP in subsequent columns.
youll see the WebForm
response back to the requesting
Designer, where you can
Conclusion
client. ASP.NET is no different.
drop components just
Thats the 10,000 feet view
like youre used to for
of the ASP.NET architecture,
Windows applications.
and a rather short one at that,
The Project Manager shows that you have five files to
given the vastness that is ASP.NET. The entire framework
start out with, two of which are closely associated with
is designed to make things quite easy on you via the IDE,
each other. They are discussed in Figure 2.
while at the same time giving you the power and flexibility
to control the entire process. In addition, the architecture
From this humble beginning, you can add more pages and is designed to be extensible and customizable, just like the
code to build your dream Web site. Exactly how that gets
venerable VCL. Building custom ASP.NET controls is straightdone will obviously be the subject of future columns.
forward, and if youve done any component building in
Delphi before, ASP.NET control development should be quite
As far as the .NET Framework itself is concerned, all of
familiar.
the functionality of ASP.NET resides in the System.Web
namespace and the sub-namespaces within it. The table
And this column, in the coming issues, will show you how to
in Figure 3 lists some, but not all, of the namespaces
do all that, and more. That is, if the fame and glory of havwithin System.Web.
ing my own column doesnt go to my head and I start missing deadlines like all the famous writers do. (Note to Editor: I
Any Web application development framework is really
made that last part up. Really.)
an elaborate way to accept HTTP requests, process those
requests, and provide an HTTP response back to the
requesting client. ASP.NET is no different. Pursuant to
that, the System.Web namespace provides a class named
HTTPContext that holds all the information about the
HTTP request, and its subsequent response, in the aptly
named HTTPRequest and HTTPResponse properties. These
properties are classes that give you total control of the
Nick Hodges is the Chief Technology Officer for Lemanix Corporation
whole HTTP request/response process.
(www.lemanix.com), a Borland Solutions Partner in the Twin Cities of
21
N E W
&
U S E D
By Mike Riley
Shell+
Extend the Power of Delphi to the Windows Desktop Shell
Figure 1: Over 30 components populate the two Shell+ tabs installed in the Delphi IDE.
New
&
Used
Shell+
23
Conclusion
Documentation quality issues notwithstanding, Shell+ is a
welcome addition to a Delphi developers component library.
While a few freeware and shareware components have
attempted some of the features advertised by the product, no
commercial offering has approached the breadth of Windows
shell manipulation that Shell+ provides. Interested developers are encouraged to check out an animated demonstration
of the product at www.shellplus.com/flash-demo.html to
experience the depth of capabilities this useful set of components can provide.
N E W
&
U S E D
By Mike Riley
24
New
&
Used
it nonetheless requires
unnecessary mouse clicks
that could have easily been
avoided with a loop flag
checking if the function
were really analyzing the
last control on the form.
This seemingly minor logic
omission becomes a major
annoyance on forms with
a dozen controls.
Finally, the utility leverages Addictive Softwares
Addict Spell Check 3 as the
spelling engine. Frankly,
the English dictionaries
that are included are practically useless because of
the limited range of words
they contain and thus the
inability to suggest spelling
replacements. For example,
the misspelling of maintainance returned no
suggestions (see Figure 1).
Instead of being forced to
Figure 1: Spell checking an entire form is as simple as pressing the hard-coded CAQ. However, the spelling engine has
use the embedded Addict
trouble providing suggestions for many commonly misspelled words.
spell checking engine,
I wouldve preferred to
have the choice of selecting which spelling engines the utility
Conclusion
should employ. Ive spent years adding industry-specific terms
Borland fans need to recognize the efforts of fellow Delphi
to Microsoft Words spelling dictionary; the thought of repopu- developers seeking to solve problems of their peers. Howlating a different, less powerful spelling file is something for
ever, commercializing those efforts only succeeds when
which I have neither the time nor interest.
the solution elegantly delivers on its promise. Delphi Spell
Checker is a well-meaning attempt to minimize spelling
Although I applaud Spline for their recognition of the need errors, but its execution stumbles in embarrassing ways. If
to provide multi-language dictionary support, this feature
Spline redesigned the utility to address the shortcomings
doesnt provide any type of language translation. Basically, identified in this review, as well as exceed expectations of
the design allows Spline to market the utility internationwhat a spell checking utility targeting the Delphi coding
ally, as it does not provide language translation services
community really needed, it may be an add-on worth conof any kind. In this evolving age of Web services, Spline
sidering. Until then, peer review remains the better, more
could have simply integrated an Xmethods.net Babelfish
economical alternative.
translator to provide additional value to their product, if
only as an unsupported feature.
And because the utility doesnt spell check resource files, the
multiple language support is inadequate for those developers seeking efficient localization capabilities; the only way
to leverage Delphi Spell Checker is if the text in question is
embedded in the control. As anyone who has developed a
localized Delphi application knows, this type of practice is
greatly discouraged when designing multi-language support in
an application because of the inability to contain all resources
into a single manageable location.
25
F I L E
N E W