Chapter 14 Analysis Several Midas Demo Procedure Midas is a referusion of Multi-Tier Distributed Application Services Suite, a key technique for Delphi 4. For beginners, Midas has considerable difficulty, so this chapter analyzes several MIDAS demonstration procedures to help readers understand and master MIDAS technology.
Unlike the general database application, only the project can be opened, compiled and running, compiling and running the "thin" client when the application server is running.
14.1 An example of an Activeform
Delphi 4 can bring the distributed database structure to the Internet / Intranet, which embeds the "thin" client as an ActiveForm into the web page, and then executes locally.
This section analyzes an ActiveForm's demonstration program, and the project name is EMPEDITX, which can be found in the C: / Program Files / Borland / Delphi4 / Demos / Midas / ActiveFM directory. Its main form is shown in Figure 14.1.
Before opening this project, you must compile, run the Server project in the C: / ProgramFiles / Borland / Delphi4 / Demos / Midas / Empedit directory, which is an application server, as shown in Figure 14.2.
Here is to explain, on the application server, introduce the data set with a TQUERY component, and its SQL statement is as follows:
SELECT * from Employee
On this ActiveForm, there is a TDCOMCONNECTION component for connecting the application server in a DCOM. Its ServerName property is set to serv.empserver, and its ServerGUID is set to {53BC6562-5B3E-11D0-9FFC-00A0248E4B9A}.
ActiveForm is introduced from the application server from the application server with the TclientDataSet component, and its RemoteServer property is set to the name of the MidasConnection, which is set to Empquery, which is the TQuery component on the application server, by it provides an iProvider interface.
There are several data controls on ActiveForms, which are used to obtain data through a TDataSource component. In addition, there is a TDBNAVigator component on the ActiveForm for browsing the data set.
Since the demonstration program to be introduced this section is an ActiveForm, most of its code is related to the type library, we only put some of the "拎" of the MIDAS technology.
When the user clicks the "Get Employees" button on ActiveForm, the data is retrieved from the application server. Each retrieved number of records depends on the PacketRecords attribute of the TclientDataSet component.
Procedure Tempeditform.QueryButtonClick (Sender: TOBJECT);
Begin
Employees.Close;
{Employees is the name of the TclientDataSet component} E
MPLOYEES.OPEN;
END;
When the user clicks the "Update Employees" button on ActiveForm, write the user's modification of the data to the data set.
Procedure Tempeditform.UpdateButtonClick (Sender: TOBJECT);
Begin
Employees.Applyupdates (-1);
END;
Cancel the user's modification of the data when the user clicks on the "undo Last Change" button on ActiveForm.
Procedure Tempeditform.undobuttonClick (Sender: TOBJECT);
Begin
Employees.undolastChange (TRUE);
END;
In order to test this ActiveForm, you first need to post it onto the web server for download. To do this, use the "Web Deployment Options" command on the "Project" menu to set the option for the web publishing, mainly to specify the URL on the ActiveForm on the web server. Then publish the ActiveForm on the web server using the "WebDeploy" command on the Project menu.
14.2 Demonstration program for dynamically transmitting SQL statements
This section analyzes an exemplary program that dynamically transmits the SQL statement, which can be found in the C: / Program Files / Borland / Delphi4 / DEMOS / MIDAS / Adhoc directory.
This program is divided into two parts: application servers and client programs. When the client calls DataRequest request data via the iProvider interface, pass the SQL statement entered to the application server, so that the TQuery component on the application server can query the database according to the user's requirements, which is the basic idea of this demonstration program.
Firstly, the application server is first, and the data module is seen, as shown in Figure 14.3.
Figure 14.3 Data module
There are such components on the data module:
A TSESSION component, its sessionName property is set to session1_2.
A TDATABASE component, its sessionname property is set to session1_2, and defines a dedicated alias called Adhoc.
A TQUERY component, its DatabaseName property is set to Adhoc, and its sessionName property is also set to session1_2, and its SQL property is empty because the SQL statement is transmitted dynamically by the client program.
A TPROVIDER component, its DataSet property is set to the name of the TQUERY component.
Now we temporarily see the data module, then look at the main form of the application server, as shown in Figure 14.4.
Figure 14.4 Main Form for Application Server
Two counts are displayed on the main form, one is the number of customers currently connected to the application server, and the other is the number of queries that have already been executed (queries).
What to use to determine the current customer, this is related to the instance of the data module. We can return to the unit of the data module to see its initialization code:
INITIALIZATION
TcomponentFactory.create (COMSERVER, TADHOCQUERYDEMO,
Class_adhocQueryDemo, CIMULTIINSTANCE
End.
It can be seen that the instance of this data module is set to CIMULTIINSTANCE, indicating that a new instance of the data module will be created whenever a client is connected to the application server. Therefore, the number of instances of data modules is the current number of customers. How do I have the count number of data modules? Very simple, just handle the oncreate event of the data module.
Procedure TadhocqueryDemo.adhocqueryDemOCreate (Sender: TOBJECT);
Begin
Mainform.UpdateClientCount (1);
END;
When a customer exits the connection, an instance of a data module will be deleted, and the ONDESTROY event of the data module is triggered:
Procedure TadhocqueryDemo.adhocqueryDemodeStroy (Sender: TOBJECT);
Begin
Mainform.UpdateClientCount (-1);
Where the UpdateClientCount function is defined in the unit of the main form:
Procedure TMAINFORM.UPDATECLIENTCOUNT (INCR: Integer);
Begin
Fclientcount: = fclientcount inCr;
Clientcount.caption: = INTOSTR (fclientcount);
END;
Please read by pay attention to the role of the INCR parameter. How do I have the number of queries that have been implemented? It is very simple, as long as the number of TQuery components is activated. Therefore, the program handles the AfterOpen event of the TQuery component. Procedure TadhocqueryDemo.adhocQueryAfteropen (DataSet: TDataSet);
Begin
Mainform.incquerycount;
END;
INCQUERYCOUNT is defined in the unit of the main form:
Procedure tMainform.incqurycount;
Begin
Inc (FQUERYCOUNT);
Querycount.caption: = INTTOSTR (FQueryCount);
END;
You must compile and run items for the application server before opening the customer's project. Ok, now we open the customer's project, and its main form is shown in Figure 14.5.
This client is connected to the application server with a TDCOMCONNECTION component, and its ServerName property is set to serv.adhocquerydemo. The client program introduces a data set from the application server with the TclientDataSet component, and its RemoteServer property is set to the name of the TDCOMConnection component, which is set to AdhocQuery, which is the iProvider interface output by the application server.
There is a grid on the client program for displaying data, and the grid and the data set are connected via the TDataSource component. In addition, there is a multi-line text editor on the client program to enable the user to enter the SQL statement. There is a combo box to select the database you want to access. We still start with the handle to handle the oncreate event.
Procedure TFORM1.FormCreate (Sender: TOBJECT);
VAR i: integer; dbnames: olevariant;
Begin
Remoteserver.Connected: = true;
DBNAMES: = RemoteServer.AppServer.getDatabaseNames;
IF VarisArray (DBNAMES) THEN
For i: = 0 to VararrayHighBound (dbnames, 1) DO
DatabaseName.Items.Add (dbnames [i]);
DatabaseNameClick (Self);
END;
First, set the connect attribute of the TDCOMConnection component to True and the application server will be connected. The AppServer property of the TDCOMConnection component will return the interface of the data module on the application server, and the remote data module can be called by this interface, such as GetDatabaseNames. GetDatabaseNames is defined in the data module unit of the application server:
Function TadhocqueryDemo.getDatabaseNames: olevariant;
VAR i: integer;
DBNAMES: TSTRINGS;
Begin
DBNAMES: = TSTRINGLIST.CREATE;
Trysession1.GetDatabaseNames (DBNAMES);
Result: = VararrayCreate ([0, DBNAMES.COUNT - 1], Varolestr);
For i: = 0 to dbnames.count - 1 doreSult [i]: = dbnames [i];
FinallyDbnames.free;
END;
END;
The role of the getDatabaseNames function is to return an array that consists of all the unspecified alias and BDE session object alias. Now let's go back to the client program, after calling the GetDatabaseNames function of the data module, retrieve the retrieved alias to the combo box in the upper right corner of the form, then call the DatabaseNameClick function.
Procedure TFORM1.DATABASENAMECLICK (Sender: TOBJECT);
Var Password: String;
Begin
If DatabaseName.Text <> '' THEN
Begin
ClientData.Close;
Try
RemoteServer.AppServer.SetDatabaseName (DatabaseName.Text, '');
Except
ON E: Exception Doif E.MESSAGE = 'Password Required' Then
Begin
If INPUTQUERY (E.MESSAGE, 'Enter Password', Password) THEN
RemoteServer.AppServer.SetDatabaseName (DatabaseName.Text, Password);
End
Else
Raise;
END;
END;
END;
The purpose of calling DatabaseNameClick is to connect the application server to another database, which requires the interface of the data module through the App Server property, and then calls the setDatabaseName of the data module unit. SetDatabaseName is defined in the data module unit of the application server:
Procedure TadhocqueryDemo.SetDatabaseName (const dbname, password: wideString);
Begin
Try
Database1.close;
Database1.aliasname: = dbname;
IF Password <> '' Then
Database1.params.values ['password']: = password;
Database1.open;
Except
{If the database opens fail, it is probably because the database needs a password}
ON E: EdbEngineError Doif (Password = ') Then Raise Exception.create (' Password Required ') ELSE
Raise;
END;
END;
The role of SetDatabaseName is to modify the AliasName property of the TDatabase component, then connect the new database. If it fails, it triggers an exception. During the DatabaseNameClick process of the client program, if an exception occurs, you will pop up a input box, let the user enter the password, then call the setDatabaseName of the data module again.
When the user entered the SQL statement in the "Query" box, click the "Run Query" button to execute this query. The problem is that only the application server can execute the query, how do the client passed the SQL statement to the application server? This is the key to this demonstration process. Procedure TFORM1.RunbuttonClick (Sender: TOBJECT);
Begin
ClientData.Close;
ClientData.Provider.DataRequest (Sql.Lines.Text);
ClientData.Open;
END;
It turns out that the client program calls DataRequest via the iProvider interface to pass the SQL statement entered to the application server. The client program calls DataRequest via the iProvider interface will trigger the onDataRequest event on the application server side, let's take a look at how the application server handles the OndataRequest event.
Function TadhocqueryDemo.adhocProviderDataRequest (Sender: Tobject; Input: Olevariant): Olevariant;
Begin
Adhocquuery.sql.text: = input;
END;
At this point, a dynamically transmitted SQL statement is completed, and readers should carefully ponder the programming skills. In fact, call DataRequest via the iProvider interface can pass any information.
14.4 A Demonstration Procedure for a TCLientDataSet Function
This section describes a demonstration program that demonstrates the TclientDataSet function. The project name is Alchtest, which can be found in the C: / Program Files / Borland / Delphi4 / Demos / Midas / Alchtest directory, the main form is shown in Figure 14.6.
The overall idea of this program is that uses a multi-page control to make the user modify the attribute of the TclientDataSet or call it, and then demonstrate the modified effect in the TAB control below.
The program first made some initialization work in handling the handle of the oncreate event.
Procedure TdbClientTest.formCreate (Sender: TOBJECT);
VAR i: integer;
Begin
Database1.close;
FmaxerRors: = -1;
FPACKETRECS: = -1;
SetCurrentDirectory (Pchar (ExtractFilePath)))))))))
For i: = 0 to statusbar.panels.count - 1 DO
Statusbar.Panels [i] .text: = '';
Application.onidle: = ShowheapStatus;
Application.onhint: = onhint
StreamSettings (false);
SetEventsVisible (VIEWEVENTS.CHECKED);
END;
Among them, specifying ShowHeapStatus as a handle of the onIdle event for processing the application, specifying the handle of OnHint as the onHINT event of the application. Showheapstatus is defined in this:
Procedure TDBClientTest.showheapStatus (Sender: Tobject; Var Done: Boolean);
Begin
CAPTION: = Format ('Client Dataset Test Form - (Blocks =% D Bytes =% D)', [AllocMemcount, AllocMemsize]);
END; SHOWHEAPSTATUS's role is to display the state of the stack at the title bar of the main window in the application free, where allocMemcount is the currently allocated memory block, and AllocMemSize is the currently allocated memory length.
Onhint is defined in this:
Procedure TdbClientTest.OnHint (Sender: TOBJECT);
Begin
STATUSMSG: = Application.hint;
END;
StatusMSG is a custom property that expresss prompt information to be displayed on the status bar.
The StreamSettings function is also called in the handle that processes the OnCreate event. StreamSettings is very useful. When the form is closed, call streamSettings to save the status of some controls on the form to a configuration file. When the form is popped up, call the streamSettings to read the configuration file to initialize the control on the form.
Procedure TdbClientTest.StreamSettings (Write: Boolean);
Procedure Writestr (const Optname, value: string);
Begin
Fconfig.WritString ('settings', optName, value);
END;
Procedure WriteBool (Const Optname: String; Value: Boolean);
Begin
Fconfig.writeBool ('settings', optName, value);
END;
Function ReadStr (const option): String;
Begin
Result: = fconfig.readstring ('settings', Optname, '');
END;
Function ReadBool (const Optname: String): Boolean;
Begin
Result: = fconfig.readBool ('settings', Optname, False);
END;
Function FindPage (const pagename: string): ttabsheet;
VAR i: integer;
Begin
For i: = limited - 1 Downto 0 DO
Begin
Result: = aaselector.pages [i];
IF result.caption = PageName dam
END;
Result: = proviDERPAGE;
END;
Procedure ProcessComponents (Components: array of tcomponent);
VARI: Integer;
Begin
IF WRITE THEN
Begin
For i: = low (components) to high (components) do
IF Components [I] IS TCUSTOMEDIT THEN
With TEDIT (Components [i]) Do Writestr (Name, Text)
Else IF Components [I] IS TCOMBOBOX THEN
With tdbcombobox (Components [i]) Do Writestr (Name, Text)
Else IF Components [I] is Tcheckbox Then
With Tcheckbox (Components [i]) Do WriteBool (Name, Checked) Else IF Components [i] is Tong Then
With Taction (Components [i]) Do WriteBool (Name, Checked)
Else if Components [i] is tpagecontrol.
WITH TPAGECONTROL (Components [i]) DowritStr (Name, ActivePage.caption);
END;
Else
Begin
For i: = low (components) to high (components) do
IF Components [I] IS TCUSTOMEDIT THEN
WITH TEDIT (Components [i]) Do Text: = ReadStr (NAME)
Else IF Components [I] IS TCOMBOBOX THEN
With Tcombobox (Components [i]) Do Text: = ReadStr (Name)
Else IF Components [I] is Tcheckbox Then
With Tcheckbox (Components [i]) Do Checked: = ReadBool (Name)
Else if Components [I] is TACTION THEN
WITH TACTION (Components [i]) Do Checked: = ReadBool (Name)
Else if Components [i] is tpagecontrol.
WITH TPAGECONTROL (Components [i]) DoctivePage: = FINDPAGE (ReadStr (Name));
END;
END;
Begin
GetConfigfile;
IF not write and (readstr ('areaselector') = ') THEN EXIT;
ProcessComponents ([AreaSelector, DatabaseName, MasterTableName, DetailTableName, MasterSQL, DetailSQL, poCascadedDeletes, poCascadedUpdates, poDelayedDetails, poDelayedBlobs, poIncludeFieldProps, poReadOnly, DisableProvider, ObjectView, SparseArrays, MixedData, FetchOnDemand, DisableProvider, ResolveToDataSet, DataRows, CreateDataSetDesc, EnableBCD, RequestLiveQuery, ViewEvents , DISPLAYDETAILS, INCLUDENESTEDOBJECT]);
END;
STREAMSETTINGS uses a Write parameter to distinguish it is to read or write. Several processes and functions are nested in StreamSettings, where Writestr, WriteBool, ReadStr, ReadBool are used to save strings and Boolean information in the configuration file, and the FindPage function search and return a specific object, and ProcessComponents It is used to access information related to the specific components.
The getConfigFile function is used to create an instance of a TiniFile object (if there is not yet created).
Function TDBClientTest.getConfigfile: tinifile;
Begin
IF fconfig = nil dam
Fconfig: = tinifile.create (ChangefileExt (paramstr (0), '.ini'); result: = fconfig;
END;
Please read the reader to pay attention to how streamSettings calls the ProcessComponents function. ProcessComponents needs to pass an array, the elements in the array are the names of some controls on the form.
Let's turn to the "Provider" page to see how to specify the database and establish the master / detail relationship, as shown in Figure 14.7.
Figure 14.7 "provider" page
The "Database" box is used to specify the database to be accessed. When the user pulls this box, the OONDROPDOWN event will be triggered. If the "Database" box is still empty, call the TSession's getDatabaseNames function to fill all the defined BDE alias and dedicated alias into the "Database" box.
Procedure TdbClientTest.databaseNamedropdown (Sender: TOBJECT);
Begin
If DatabaseName.Items.count = 0 THEN
Session.getDatabaseNames (DatabaseName.Items);
END;
When the user selects an alias in the "Database" box, the ONCLICK event will be triggered. At this point, you call the CheckDatabase to connect to another database. Since the database has changed, the content within the "Master / DetailTables" box should be clear.
Procedure TdbClientTest.DatabaseNameClick (Sender: TOBJECT);
Begin
IF (DatabaseName.Text <> ') and not databasename.droppedDown then
Begin
CheckDatabase (TRUE);
MASTERTABLENAME.ITEMS.CLEAR;
MASTERTABLENAME.TEXT: = '';
DetailTableName.Text: = '';
ClientData.Close;
END;
END;
Users can also type a database alias directly in the "Database" box, then press Enter to trigger the ONKEYPRESS event.
Procedure TDBClientTest.DatabaseNameKeyPress (Sender: Tobject; Var Key: char);
Begin
If key = # 13 THEN
Begin
If DatabaseName.droppedDown Then
DatabaseName.DroppedDown: = false;
DatabaseNameClick (Sender); key: = # 0;
END;
END;
Ok, let us see how CheckDatabase is defined:
Procedure TDBClientTest.checkDatabase (Closefirst: Boolean);
Var spassword, susename: String;
Begin
If not closefirst and database1.connected and (database1.aliasname = DatabaseName.Text) THEN EXIT;
Database1.close;
Database1.aliasname: = DatabaseName.Text;
Session.getaliasparams (Database1.Params); if Database1.Params.indexOfName ('PATH') = -1 Then
Begin
Spassword: = configfile.readstring ('passwords', Database1.aliasName, '');
IF spassword = '' THEN
Begin
SUSERNAME: = Database1.Params.Values ['user name'];
IF not logindialog ('DatabaseName.Text', SUSERNAME, SPASSWORD).
Database1.params.values ['user name']: = susename
END;
Database1.params.values ['password']: = spassword;
END;
If enablebcd.checked the database1.params.add ('enable bcd = true ";
Database1.open;
IF database1.issqlbased and (spassword <> '). Thenfigfile.writestring (' Passwords', Database1.aliasName, Spassword);
END;
CheckDatabase is used to connect a user specified database. If the current connection is the database specified by the user, CheckDatabase does not do anything. If not, first call the Close of the TDATABASE component to disconnect the connection with the database, and then set the AliasName property of the TDATABASE component to the alias selection, and call the GetAliasParams of the BDE session to remove the parameters of this alias.
Note that for the local database, only one path parameter, and for the SQL database, there are several parameters, so there is no PATH parameter to distinguish the local database and SQL database. If it is a SQL database, set the user name and password parameter to give the username and password. If the "enablebcd" command on the "Settings" menu is selected, an Enable BCD parameter is added and the value is set to True. Then call the Open to reconnect the database.
This program also allows customers to select the master table and Detail table in the "master / detail" relationship, which is selected in the "Master / Detail Tables" box, where one of the combo box is used to select the master table, one of the following combination Box is used to select the Detail table. When the user selects a table in the combo box, the ONCLICK event will be triggered.
Procedure TdbClientTest.mastertableNameClick (Sender: TOBJECT);
Begin
With sender as tcombobox do
IF not DroppedDown and (mastertable.tablename <> text). ..Execute;
END;
When the user pulls a combo box in the "Master / Detail Tables" box, the OONDROPDOWN event will be triggered. At this point, you will fill in the name of all the tables in the current database to the combo box for the user selection. Procedure TdbClientTest.MasterTableNameDropdown (Sender: TOBJECT);
Begin
CheckDatabase (false);
With sender as tcombobox do
IF (items.count <1) and (Database1.AliasName <> ') THEN
Session.gettablenames (Database1.DatabaseName, '', True, False, Items);
END;
The user can also type a table's name directly within a combination in the "master / Detail Tables" box, then press ENTER to trigger the ONKEYPRESS event.
Procedure TdbClientTest.MasterTableNameKeyPress (Sender: Tobject; Var Key: char);
Begin
If key = # 13 THEN
Begin
With sender as tcombobox do
IF DroppedDown the DroppedDown: = false;
OpenTable.execute;
Key: = # 0;
END;
END;
Note: The above is in the case where the combo box is selected as an example. In fact, the operation of the Detail table is selected is exactly the same, the code is as follows.
Procedure TdbClientTest.detailTableNameClick (Sender: TOBJECT);
Begin
With sender as tcombobox do
IF not DroppedDown and (detailtable.tablename <> text) THEN
OpenTable.execute;
END;
In the above event handles, OpenTable is a list of action, which is a newly added function of Delphi 4. Double-click the TactionList component on the form, open a editor as shown in Figure 14.8.
Figure 14.8 Action List Editor
In this editor, find this action, then you can find that the code that performs this action is the OpenTableExecute function in the Object Observer.
Procedure TDBClientTest.OpenTableExecute (Sender: TOBJECT);
Begin
ClearEventlog.execute;
If MASTERTABLENAME.TEXT <> '' Ten OpenDataSet (MASTERTABLE);
END;
OpenDataSet is defined in this:
Procedure tdbclientTest.Opendataset (Source: TDBDataSet);
Begin
Screen.cursor: = CRHOURGLASS;
Try
ClientData.Data: = NULL;
Source.close;
IF not disableprovider.checked kil
Begin
BDEPROVIDER.DataSet: = Source;
SetProviderOptions;
ClientData.ProviderName: = bdeprovider.name;
ActiveDataSet: = ClientData; END
Else
ActiveDataSet: = Source;
Mastergrid.setfocus;
Statusmsg: = 'Dataset Opened';
FinallyScreen.cursor: = crdefault;
END;
StreamSettings (TRUE);
END;
OpenDataSet decides whether to use the TPROVIDER component through a checkbox called DisableProvider. If you do not select the "Disable Provider" check box, you represent the use of the TPROVIDER component, at which point the TProvider component is set to MASTERTABLE, then call SetProviderOptions to set the option to set the TPROVIDER component, then set the TclInderName property of the TclInderDataSet component Specify this TPROVIDER. The component, finally set the ActiveDataSet variable to this TclientDataSet component. If the user selects the "Disable Provider" check box, indicates that the TPROVIDER component is not used, and then the ActiveDataSet is set directly to MASTABLE.
SetProviderOptions is defined in this:
Procedure tdbclientTest.SetProviderOptions;
Var OPTS: TPROVIDEROPTIONS;
Begin
OPTS: = []; if podelayeddetails.checked the
Include (OPTS, PofetchDetails);
If Podelayedblobs.Checked Then Include (OPTS, PofetchBlobsondemand);
If PocaScadeddeletes.checked Ten Include (OPTS, POCASCADELES);
If PocaScadedupdates.checked THEN INCLUDE (OPTS, POCASCADEUPDATES);
IF poreadonly.checked kilod (OPTS, Provider.poreadOn ";
IF poincludefieldprops.checked kilod (OPTS, POINCFIELDPROPS);
BDEPROVIDER.OPTIONS: = OPTS;
END;
SetProviderOptions actually sets whether to set the Options property of the TPROVIDER component based on some subcommands of the "Provider Options" command on the "Settings" menu. This program also allows users to enter SQL statements in the "Master / Detail Queries" box. When the user enters the SQL statement and presses the Enter key, the ONKEYPRESS event will be triggered.
Procedure TdbClientTest.Master: Tobject; Var Key: char);
Begin
If key = # 13 THEN
Begin
OpenQuery.execute;
Key: = # 0;
END;
END;
Among them, OpenQuery is also an action, executing it is the OpenQueryExecute function. OpenQueryExecute is defined in this:
Procedure TDBClientTest.openQueryexecute (Sender: TOBJECT);
Begin
IF Uppercase (Copy (MasterSql.Text, 1, 6)) = 'SELECT' THENOPENDATASET (MasterQuery)
Else
Begin
CheckDatabase (false);
MasterQuery.RequestLive: = true;
MasterQuery.sql.text: = MASTERSQL.TEXT;
MASTERQUERY.EXECSQL;
Statusmsg: = format ('% d rows were affected ", [masterQuery.rowsaffected]);
END;
Events.items.
Begin
Update;
Try
Events.clear;
Finally
Events.Items.Endupdate;
END;
END;
OpenQueryExecute first determines whether the user entered SQL statement is SELECT. If so, call the OpenDataSet to perform the SELECT statement. If not, call the execSQL to execute the SQL statement.
When the user flies to the "fields" page, the ONSHOW event of the Fieldspage (TtabSheet object) is triggered, and the fields and field definition object names in the data set are displayed in two multi-line text editors, as shown in Figure 14.9 .
Figure 14.9 "FIELDS" page
Procedure TdbClientTest.fieldspageShow (Sender: TOBJECT);
Procedure WriteFullNames (Fields: Tfields);
VAR i: integer;
Begin
For i: = 0 to Fields.count - 1 DO
With Fields [i] DO
Begin
FieldList.Lines.Add (Format ('% d)% s', [FieldNo, Fullname]);
IF fields [i] .dattype in [ftadt, ftarray] THEN
WriteFullNames (Tobjectfield (Fields [i]). Fields);
END;
END;
Procedure WriteLists (DataSet: TDataSet);
VAR i: integer;
Begin
FieldList.clear;
For i: = 0 to dataset.fieldlist.count - 1 do
With dataset.fieldlist do
FieldList.Lines.Add (Format ('% D)% s', [Fields [i] .fieldno, Strings [i]]);
FieldDeflist.clear;
Dataset.fielddefs.updated: = false;
DataSet.fieldDeflist.Update;
For i: = 0 to dataset.fielddeflist.count - 1 do
With dataset.fielddeflist do
FieldDeflist.Lines.Add (Format ('% D)% s', [FieldDefs [i] .fieldno, Strings [i]]);
END;
Var DataSet: TDataSet;
Begin
Dataset: = dbnavigator1.datasource.datan;
IF assigned (dataset) and dataset.active the
Begin
WriteLists (Dataset)
End
Else
Begin
CheckDatabase (false);
MASTERTABLE.TABLENAME: = MASTERTABLENAME.TEXT; WRITELISTS (MASTERTABLE);
END;
END;
The first thing to explain is that WriteFullNames is nestled in FieldspageShow, in fact, WriteFullNames is completely redundant. FieldspageShow first gets the current data set. If the current data set is turned on, call the WriteLists to display the list of field objects and field definition objects. If the current data set is not open, the list of field objects and field definition objects in MASTERTABLE is displayed. When the user flies to the "indexes" page, the ONSHOW event of IndexPage will be triggered. At this time, the index in the current data set is column, and the user can create a new index or delete an index. "Indexes" page is shown in Figure 14.10.
Figure 14.10 "INDEXES" page
Procedure TdbClientTest.indexpageShow (Sender: TOBJECT);
Begin
IF not assigned (activeDataSet) or not activeDataSet.Active Then
OpenTable.execute;
RefreshindexNames (0);
END;
IndexpageShow first checks if there is a dataset. If not, open the dataset of the OpenTable code, then call the RefreshIndexNames function listing all index names.
Procedure tdbclientTest.refresh IndexNames (NewItemIndex: Integer);
VAR i: integer;
Indexdefs: TINDEXDEFS;
Begin
Indexlist.clear;
If ActiveDataSet = MASTERTABLE THEN
Indexdefs: = MASTERTABLE.INDEXDEFS
Else
INDEXDEFS: = ClientData.indexdefs;
Indexdefs.update;
For i: = 0 to indexdefs.count - 1 do
IF indexdefs [i] .name = '' Then indexlist.Items.add ('
')
Else
IndexList.Items.Add (Indexdefs [i] .name);
IF indexlist.items.count> 0 THEN
Begin
IF newItemIndex Indexlist.ItemIndex: = newItemIndex ElseIndexList.itemindex: = 0; ShowIndexparams; END; END; RefreshIndexNames calls ShowIndexParams to retrieve indexes, with these options to initialize several edit boxes and check boxes on the "Indexes" page. Procedure tdbclientTest.showindexparams; varindexdef: TINDEXDEF; Begin If ActiveDataSet = MASTERTABLE THEN INDEXDEF: = Mastertable.indexdefs [indexlist.itemindex] Else Indexdef: = ClientData.Indexdefs [indexlist.itemindex]; idxCaseInsensitive.Checked: = ixCaseInsensitive in IndexDef.Options; idxDescending.Checked: = ixDescending in IndexDef.Options; idxUnique.Checked: = ixUnique in IndexDef.Options; idxPrimary.Checked: = ixPrimary in IndexDef.Options; IndexFields.Text: = IndexDef .Fields; descfields.text: = indexdef.descfields; Caseinsfields.text: = indexdef.caseinsfields; END; If the user selects another index in the list box, you should refresh these options accordingly. Procedure TdbClientTest.indexlistClick (Sender: TOBJECT); Begin If ActiveDataSet = MASTERTABLE THEN MASTERTABLE.INDEXNAME: = MASTERTABLE.INDEXDEFS [INDEXLIST.ItemIndex] .name Else ClientData.indexName: = ClientData.indexdefs [indexlist.itemindex] .name ShowIndexparams; END; If you want to create a new index, the user must set an index option in advance, then click the "CreateIndex" button. Procedure TDBClientTest.createIndexclick (Sender: TOBJECT); Var indexname: string; options: tindexoptions Begin IndexName: = Format ('Index% D', [IndexList.Items.count 1]); IF INPUTQUERY ('Create Index', 'Enter IndexName:', IndexName) THEN Begin Options: = []; If idxcaseinsensitive.checked kilod (options, ixcaseinsensitive); If idxdescending.checked dam, ixdescending; If idxunique.checked kiln ket (options, ixunique); If idxprimary.checked kilodes (options, ixprimary); If ActiveDataSet = MASTERTABLE THEN Begin MASTERTABLE.CLOSE; MASTERTABLE.AddIndex (IndexName, Indexfields.Text, Options, Descfields.Text); MASTERTABLE.OPEN End Else ClientData.AddIndex (IndexName, Indexfields.Text, Options, Descfields.Text, Caseinsfields.Text); Statusmsg: = 'index created'; Refresh IndexNames (indexlist.items.count); END; END; CreateIndexClick first pops up a input box, allows the user to enter an index name, and then set the options attribute of the index according to the options for the user. Before calling AddIndex, you should first distinguish between the current data set is MASTERTABLE or ClientData, why do you distinguish MASTERTABLE and CLIENTDATA? Because for a general data set component, the data set must be turned off before creating an index, and for the TClientDataSet component, you don't have to close the data set first. Users can also select an index first, then click the "Delete Index" button to delete this index. Procedure TdbClientTest.deleteIndexclick (Sender: TOBJECT); Begin If indexlist.itemindex> -1 then If ActiveDataSet = MASTERTABLE THEN Begin MASTERTABLE.CLOSE; MASTERTABLE.DELETEINDEX (MASTERTABLE.INDEXDEFS [INDEXLIST.ItemIndex] .name); MASTERTABLE.OPEN End Else ClientData.deleteIndex (ClientData.indexdefs [indexlist.itemindex] .name); END; Like calling addindex, first distinguishing the current data set before calling deleteIndex, is MASTERTABLE or ClientData. When the user turned to the Filters page, the filtering condition can be set, as shown in Figure 14.11. Figure 14.11 "Filters" page When the "Filters" page is just open, the ONSHOW event will be triggered so that the "Filter" box can be initialized. This uses a programming skill, first remove a field from the grid below, and then determine whether the data type of this field is ftstring, ftmemo, or ftfixedchar, if yes, the filter condition expression operator is followed. The value should be enclosed with quotation marks. Procedure TDBClientTest.FilterpageShow (Sender: TOBJECT); Var Field: Tfield; LocValue, Quotechar: String Begin IF (Filter.Text = '') And assigned (activeDataSet) and activeDataSet.Active the Begin Field: = MASTERGRID.SELECTEDFIELD; if Field = NIL THEN EXIT; WITH ACTIVEDATASET DOTRYDISABLECONTROLS; Moveby (3); Locvalue: = Field.Value; First; Finally EnableControls; END; If Field.DataType in [ftstring, ftmemo, ftfixedchar] THEN Quotechar: = '' '' Else quotechar: = ''; Filter.Text: = format ('% s =% s% s% 1: s', [field.ffullname, quotechar, locvalue]); END; END; The user can type a new filter condition in the "Filter" box, and the user inputs the filter condition expression to the current data set in the current data set by pressing the Enter or remove the input focus. When the user turned to the "Findkey" page, you can enter a key value and then search for a specific record in the data set, as shown in Figure 14.12. Figure 14.12 "FindKey" page When the user clicks the "Find Key" or "Find Nearest" button, starts searching for a specific record. Procedure TDBClientTest.FindKeyClick (Sender: TOBJECT); Begin If ActiveDataSet = ClientData Then WITH ClientData DO Begin SetKey; Indexfields [0] .sstring: = FindValue.Text; KeyExClusive: = self.keyexclusive.checked; if FindPartial.checked the keyfieldcount: = 0; If sender = self.findnearest the Gotonearest Else IF not gotokey the statusmsg: = 'not found'; End Else if ActiveDataSet = MASTERTABLE THEN With masteable do Begin SetKey; Indexfields [0] .sstring: = FindValue.Text; KeyExClusive: = self.keyexclusive.checked; If FINDPARTIAL.CHECKED THEN Keyfieldcount: = 0; If sender = self.findnearest the GotoneareAreSt Else IF gotoKey TenStatusmsg: = 'Record Found' Else Statusmsg: = 'NOT FOUND'; END; END; First, to distinguish whether the current data set is ClientData or MASTERTABLE, calling SetKey to enter the DSSetKey state, assign the user input to the first field in the index. Then, according to the Sender parameter, it is determined that the user presses the "Find Key" button or the "Find Nearest" button. If it is the latter, call Gotonearest if it is the former, call GotoKey, and finally displays the relevant information according to the return value of GotoKey. When the user turned to the "Locate" page, the LocatePage (TTABSHEET object) will trigger the LocatePage (TTABSHEET object), and the program selects the fields selected in the raster as a key field. "Locate" page is shown in Figure 14.13. Figure 14.13 "Locate" page Procedure TDBClientTest.locatePageShow (Sender: TOBJECT); Var field: tfield; Begin IF (ActiveDataSet <> nil) and activeDataSet.Active the Beginfield: = Mastergrid.selectedfield; If Locatefield.Items.count = 0 THEN LocatefieldDropdown (Locatefield); IF (locatefield.text = ') or (locatefield.Items.indexof (field.fieldname) <1 )11Locatefield.Text: = Field.fieldName; With activeDataSet do Try DisableControls; Moveby (3); LocateEdit.Text: = Field.Value; First; Finally EnableControls; END; END; END; Users can also select a key field in the "Field" box. When the user pulls the "Field" box, trigger the onDropdown event so that the fields in the current data set can be displayed in the "field" box. Procedure TdbClientTest.locatefieldDropdown (Sender: TOBJECT); Begin ActiveDataSet.GetfieldNames (Locatefield.Items); END; When the user selects a key field and enter the key value, you can click the Locate record. Procedure TDBClientTest.locateButtonClick (Sender: Tobject); VarOptions: TlocateOptions; LocateValue: Variant; Begin Options: = []; If LOCCASEINSENSITIVE.CHECKED THEN INCLUDE (OPTIONS, LOCASEINSENSITIVE); If LocpartialKey.checked THEN INCLUDE (OPTIONS, LOPARTIALKEY); If Locatenull.checked Then LocateValue: = NULL Else LocateValue: = locateedit.text; IF ActiveDataSet.locate (Locatefield.Text, LocateValue, Options) THEN Statusmsg: = 'Record Found' Else Statusmsg: = 'NOT FOUND'; END; The previous few lines of code mainly set the option, whereile the "Null Value" check box is selected, set the key value to NULL. Then call the Locate function positioning record of the current data set, and displays the corresponding information based on the return value of the Locate function. 14.6 a login demonstration program This section analyzes a login demonstration program that can be found in the C: / Program Files / Borland / Delphi4 / Demos / Midas / Login directory. This program is divided into two parts: application servers and client programs. There is a list box on the main form of the application server, which is used to record the username you have logged in to the application server, as shown in Figure 14.16. The data module on the application server is shown in Figure 14.17. There is only one TTable component on the data module, and its DatabaseName property is set to DBDemos, and the TableName property is set to Country. There is no TPROVIDER component on the data module, which provides an IProvider interface by the TTable component. The instance of this data module is set to CIMULTIINSTANCE, which means that a new instance of the data module will be created whenever a client is connected to the application server. When the customer is no longer connected to the application server, instance of the data module is deleted. Therefore, this program uses the oncreate event of the data module to make some initialization work, using the ONDESTROY event of the data module from the list box to delete a username. Procedure Tlogindemo.logindemoCreate (Sender: TOBJECT); Begin Floggedin: = false; END; Why do you want to set the FLOGGEDIN variable to false? The reason will be explained later. Procedure Tlogindemo.logindemodestroy (Sender: TOBJECT); Begin With form1.listbox1.items do delete (Indexof (FuserName); END; Compile and run this application server. Open the customer's project, its main form is shown in Figure 14.18. The TDCOMCONNECTION component on the form is used to connect the application server, and its ServerName property is set to Server.Logindemo, and its LoginPrompt property is set to true. The RemoteServer property of the TclientDataSet component on the form specifies the TDCOMCONNECTION component, and its ProviderName property is set to Country. In addition, there is a grid on the form to display data in the data set, and an "open" button is used to open the data set. Since the LoginPrompt property of the TDCOMConnection component is set to TRUE, a "Remote Login" dialog box will pop up when the client tries to connect to the application server, requiring the user to enter the username and password. After logging in, I trigger the ONLOGIN event. In handle the handle of this event, the client obtains the interface of the data module via the appserver property to invoke the login of the data module. Procedure TFORM1.DCOMCONNECTION1LOGIN (Sender: Tobject; UserName, Password: String); Begin DCOMCONNECTION1.APPServer.login (username, password); END; Login is defined in the data module unit of the application server. Procedure Tlogindemo.login (const username, password: wideString); Begin Form1.ListBox1.Items.Add (username); Floggedin: = true; FUSERNAME: = Username; END; Login adds the username to the list box, and set the FLoggedin variable to true, indicating that the user is logged in. When the user clicks the "Open" button, call the Open of the TclientDataSet component to open the data set. Procedure TFORM1.BUTTON1CLICK (Sender: TOBJECT); Begin ClientDataSet1.Open; END; 14.7 Demonstration Procedure Demonstrate Master / Detail Relationship This section analyzes a demonstration program that demonstrates Master / Detail relationships, which can be found in the C: / ProgramFiles / Borland / Delphi4 / Demos / Midas / MSTRDTL directory. This program is divided into two parts: application servers and client programs. The application server has a form, however, this form is actually redundant. If you don't want to display, you can open the project file of the application server, join this: Application.showmainform: = false; The data module of the application server is shown in Figure 14.19. There are several components on the data module of the application server: TDATABASE components named Database, its AliasName property is set to IBLOCAL and define an app-specific alian name called ProjectDB. Its Params property provides username and password. The TTable component named Project, which is set to Projectdb, and its TableName property is set to Project (Note: INTERBASE Server must be run. The TQuery component named Employee, which is set to Projectdb, its SQL statement is as follows: select * from Employee_Project E Where E.Proj_ID =: Proj_ID The TQUERY component named EMPPROJ, which is set to Projectdb, its SQL statement is as follows: select EMP_NO, FULL_NAME FROM EMPLOYEE The TQUERY component named UpdateQuery, its DatabaseName property is set to ProjectDB, and its SQL statement is currently empty. The TPROVIDER component named ProjectProvider, which is set to Project. The TDataSource component called ProjectSource, which is set to Project. Compile and run the application server. You can now open the program of the client, and its data module is shown in Figure 14.20. Figure 14.20 Data Module There are such components on the data module of the client program: The TDCOMCONNECTION component named DCOMCONNECTION, its ServerName property is set to serv.projectddata. The TclientDataSet component called Project, its RemoteServer property is set to DCOMCONNECTION It is set to ProjectProvider. And established a permanent field object called Projectempproj, which is TDataSetField. The TDataSource component corresponding to Project is called ProjectSource. TclientDataSet components named EMP_PROJ, its RemoteServer attribute, and proviDERNAME properties are empty, but its DataSetField property is set to the field object of Projectempproj, which constitutes the master / detail relationship. The TDataSource component corresponding to EMP_PROJ is called EMPPROJSOURCE. The TClientDataSet component named Employee, which specifies the TDCOMCONNECTION component, but its ProviderName property is set to EMPLOYEE. The TDataSource component corresponding to Employee is called Employeesource. Let's see the main form of the client program, as shown in Figure 14.21. On the left, a grid only displays the Proj_Name field in the Project data set, the project name, "Product" box Displays the Product field in the Project data set, "Description" box shows the proj_desc field in the Project data set, and uses a TDBNAVIATOR component to provide the Project Data Set navigation . The raster in the lower right corner shows the value of the field called Employeename, which is a lookup field. Its lookupDataSet property is set to EMPLOYEE, and its lookupKeyField property is set to EMP_NO, its lookupResultfield property is set to full_name. When the user browses the record of the Project dataset with a navigator, the raster in the lower right corner looks for records that match the EMP_NO field from the Employee data set, and displays the full_name fields. Since the raster in the lower right corner only establishes a permanent column object, the width of this column can be set to the grid itself, which is performed in the handle of the oncreate event of the form. Procedure TclientForm.FormCreate (Sender: TOBJECT); Begin Membergrid.columns [0] .width: = membergrid.clientwidth - getSystemMetrics (SM_CXVScroll); END; Due to more than one employee in a project, in order to be eye-catching, you can bold the person in charge, which requires processing the grid's OnDrawColumnCell event. Procedure tclientform.membergriddrawcolumncell (Sender: Tobject; Const Rect: TRECT; Datacol: Integer; Column: Tcolumn; State: TgriddrawState; Begin IF dm.projectteam_leader.value = DM.EMP_Projemp_no.ient dam.style: = [fsbold]; Membergrid.defaultDrawColumnCell (Rect, Datacol, Column, State); END; How to judge the person in charge? In the Project data set, there is a Team_leader field that stores the employee number of the project leader. In the EMP_PROJ data set, there is an EMP_NO, which is also stored is also an employee number, and if the two are equal, the employee is the project person in charge. When the user clicks the "Add" button, you can add a record in the grid, which adds an employee in the project. Procedure TclientForm.Addbtnclick (Sender: TOBJECT); Begin Membergrid.setfocus; DM.EMP_PROJ.APPpend; Membergrid.editormode: = true; END; Since the grid has established a permanent column object in advance, the field of the column object specifies a lookup field, so the user can select a value from a combo box. When the user clicks on the "Delete" button, delete the current record, an employee. Procedure TclientForm.deletebtnclick (Sender: TOBJECT); Begin DM.EMP_PROJ.DELETE; END; When the user selects one of the employees first, then click the "Leader" button to set the employee as the project leader. Procedure TclientForm.leaderbtnclick (Sender: TOBJECT); VAR NEWLEADER: Integer; Begin NEWLEADER: = DM.EMP_Projemp_no.view; IF not (dm.project.state in dseditmodes) Then DM.Project.editation; dm.projectteam_leader.value: = newLerader; MEMBERGRID.REFRESH; END; After adding, deleting, or modifying the record, the user should click the "Apply Update" button to update the database. Procedure tclientform.applyupdatesbtnclick (sender: TOBJECT); Begin DM.Applyupdates; END; In the unit of the data module, ApplyUpdates is defined in this: Procedure tdm.applyupdates; Begin If Project.Applyupdates (0) = 0 THEN Project.refresh; END; It can be seen that Applyupdates of the data module also call Applyupdates of the TclientDataSet component, and set the maxErrorS parameter to 0, so that as long as the application server finds an error record, the update stops. When the user attempts to add a new project in the grid on the left, the ONNewRecord event of the TclientDataSet component is triggered. Since this grid only shows the proj_name field, the user cannot enter the value of the Proj_ID field, so the program introduces a input box in the handle that handles the ONNewRecord event, allows the user to enter the value of the Proj_id field. If the user entered by the user exceeds the length allowed by this field, it triggers an exception. If the user does not enter any characters, it also triggers an exception. Procedure TDM.ProjectNewrecord (DataSet: TDataSet); VA RVALUE: STRING; Begin IF INPUTQUERY ('Project ID ",' Enter Project ID: ', Value) THEN Begin If Length> ProjectProj_id.size Then Raise Exception.createfmt ('Project ID CAN Only BE% D Characters', [ProjectProj_id.size]); if Length (Value) = 0 THEN Raise Exception.create ('Project ID is Required'); End Else Sysutils.abort; ProjectProj_id.value: = value; END; Since a master / Detail relationship is present between the Project dataset and the Employee dataset, when a record of the Project dataset is deleted, the records associated with the Employee data set should first be removed. This application server implements this with the BeforeUpDateRecord event of the TPROVIDER component. Procedure tprojectdata.projectProviderbeforeupdateRecord; Sourceds: tclter; deltads: tclientDataSet; Updatekind: tupdatekind; var applied: boolean; Const deleteQuery = 'delete from Employee_Project Where proj_id =: projid'; Begin IF (updatekind = ukdelete) and (sources = project) thenbegin UpdateQuery.sql.text: = deleteQuery; UpdateQuery.Params [0] .sstring: = DELTADS.FIELDBYNAME ('Proj_ID'). Asstring; UpdateQuery.execsql; END; END; 14.9 Demonstration Procedure for Dynamic Setting Inquiry Parameters This section analyzes an exemplary program that dynamically sets the query parameter, which can be found in the C: / ProgramFiles / Borland / Delphi4 / Demos / Midas / SetParam directory. This program is divided into two parts: application servers and client programs. When the client is set to the TQUERY component on the application server via the Params property of the TclientDataSet component, these parameters are automatically passed to the TQuery component on the application server, which can query the database according to the user's requirements, which is the basic idea of this demonstration program. Let's analyze the application server, first look at its data module, as shown in Figure 14.24. Figure 14.24 Only one TQuery component on the data module data module, its DatabaseName property is set to DBDemos, its SQL statement is as follows: SELECT * home "=: start_date and event_date <=: end_date order by Event_date It can be seen that there are two parameters in this SQL statement, one is: start_date, the other: End_date. Now we temporarily regardless of the data module, then look at the main form of the application server, as shown in Figure 14.25. Figure 14.25 Main Form for Application Server Two counts are displayed on the main form, one is the number of customers currently connected to the application server, and the other is the number of queries that have already been executed (queries). What to use to determine the current customer, this is related to the instance of the data module. We can return to the unit of the data module to see its initialization code: INITIALIZATION TcomponentFactory.create (COMSERVER, TSETPARAMDEMO, Class_setParamDemo, CIMULTIINSTANCE End. It can be seen that the instance of this data module is set to CIMULTIINSTANCE, indicating that a new instance of the data module will be created whenever a client is connected to the application server. Therefore, the number of instances of data modules is the current number of customers. How do I have the count number of data modules? Very simple, just handle the oncreate event of the data module. Procedure TsetParamDemo.SetParamDemoCreate (Sender: TOBJECT); Begin Mainform.UpdateClientCount (1); END; When a customer exits the connection, an instance of a data module will be deleted, and the ONDESTROY event of the data module is triggered: Procedure TsetParamDemo.SetParamDemoCreate (Sender: TOBJECT); Begin Mainform.UpdateClientCount (1); END; Where updateClientCount is defined in the unit of the main form: Procedure TMAINFORM.UPDATECLIENTCOUNT (INCR: Integer); Begin Fclientcount: = fclientcount inCr; Clientcount.caption: = INTOSTR (fclientcount); END; Note the role of the INCR parameter. How do I have the number of queries that have been implemented? It is also very simple, as long as the number of TQUERY components is activated. Therefore, the program handles the AfterOpen event of the TQuery component. Procedure TsetParamDemo.Eventsafteropen (DataSet: TDataSet); Begin Mainform.incquerycount; END; INCQUERYCOUNT is defined in the unit of the main form: Procedure tMainform.incqurycount; Begin Inc (FQUERYCOUNT); Querycount.caption: = INTTOSTR (FQueryCount); END; Compile and run this application server. Open the customer's project, its main form is shown in Figure 14.26. There is a TDCOMConnection component on the form for connecting the application server, with a TclientDataSet component called Events for introducing a data set. "Starting Date" box is used to enter the value of the start_date parameter, "Ending Date" box is used to enter the value of the End_Date parameter. The middle grid is used to display the result of the query. The "Description" box is used to display the value of the Event_Description field. The "photo" box is used to display the value of the Event_Photo field. The client program initializes the "Starting Date" box and "endingDate" box in the handle of the oncreate event of the form. Procedure TFORM1.FormCreate (Sender: TOBJECT); Begin Startdate.text: = DATETOSTR (Encodedate (96, 6, 19)); Enddate.text: = DATETOSTR (Encodedate (96, 6, 21)); END; Users can re-enter other dates in these two boxes and click the "Show Events" button. Procedure TFORM1.SHOWEVENTSCLICK (Sender: TOBJECT); Begin Events.close; Events.Params.Parambyname ('start_date'). Asdatetime: = strtodatetime (startdate.text); Events.Params.Parambyname ('end_date'). Asdatetime: = stratodatetime (enddate.text); Events.open; END; First, to call the TclientDataSet component, then set the value of the start_date parameter and the end_date parameter, finally, call the Open of the TclientDataSet component to open the dataset, this time, these two parameters are automatically passed to the application server. TQuery component.