client data set in detail5

9
Navigating and Editing a ClientDataSet By: Cary Jensen  Abstract: You navigate and edit a ClientDataSet in a manner similar to how you navigate and edit almost another other dataset. This article provides an introductory look at basic ClientDataSet navigation and editing . I usually try to start from the beginning, covering the more basic techniques before continuing to the more advanced, and that has been my plan with this series. In the articles that precede this one I have provided a general introduction to the use and behaviors of a ClientDataSet, as well as how to create its structure and indexes. In this installment I will take an introductory look at the manipulation of data stored in a ClientDataSet. Topics to be covered include basic programmatic navigation of the data in a ClientDataSet, as well as simple editing operations. The next two articles in this series will demonstrate record searching and ranges and filters. Only after these foundation topics are covered will I continue to the more interesting things that you can do with a ClientDataSet, such as creating nested datasets, cloning cursors, defining aggregate fields, and more. For those of you who are already well versed in working with datasets, you will only need to quickly skim through this article to see if there is something that you find interesting. If you are fairly new to dataset programming, however, this article will provide you with essential information on the use of Client DataSets. As an add ed benefit, most of these techniques are appropriate for any other datasets that you may have a chance to use. While this article focuses primarily on the use of code to navigate and edit data in a ClientDataSet, a natural place to begin this discussion is with Delphi data-aware controls and the navigation and editing features they provide. Navigating with Data-Aware Controls There are two classes of controls that provide data navi gation. The first class is navigation-specific controls. Delphi provides you with one control in this category, the DBNavigator. The DBNavigator, shown in the following image, provides a VCR-like interface for navigating data and managing records. Reco rd navigation is provided by the Fi rst, Next, Prior, and Last buttons. Record management is provided by the Edit, Post, Cancel, Delete, Insert, and Refresh buttons. You can control which buttons are displayed by a DBNavigator through its VisibleButtons property. For example, if you are using the DBNavigator in conjunction with a ClientDataSet that reads and writes its data from a local file (Borland calls this technology MyBase), you will want to remove the nbRefresh flag from the VisibleButtons property, since attempting to Refresh a ClientDataSet that uses MyBase raises an exception. Another DBNavigator property whose default value you may want to change is ShowHint. Some users have difficulty interpreting the glyphs on the DBNavigator's buttons. For those users, setting ShowHint to True supplements the glyphs with popup help hints. You can control the text of these hints by editing the Hints property. The second category of controls that provide navigation is the multi-record controls. Delphi includes two: the DBGrid and DBCtrlGrid. A DBGrid displays data in a row/column format. By default, all fields of the ClientDataSet are displayed in the DBGrid. You can control which fields are displayed, as well as specific column characteristics, such as color, by editing the DBGrid's Columns collection property. The following is an example of a DBGrid. avigating and Editing a ClientDataSet http://dn.codegear.com/print/29122 1 of 9 20.11.2008 17:20

Upload: yc1965

Post on 30-May-2018

220 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 1/9

Navigating and Editing a ClientDataSetBy: Cary Jensen

 Abstract: You navigate and edit a ClientDataSet in a manner similar to how you navigate and edit 

almost another other dataset. This article provides an introductory look at basic ClientDataSet 

navigation and editing.

I usually try to start from the beginning, covering the more basic techniques before continuing to the

more advanced, and that has been my plan with this series. In the articles that precede this one I

have provided a general introduction to the use and behaviors of a ClientDataSet, as well as how to

create its structure and indexes. In this installment I will take an introductory look at the

manipulation of data stored in a ClientDataSet. Topics to be covered include basic programmatic

navigation of the data in a ClientDataSet, as well as simple editing operations. The next two articles inthis series will demonstrate record searching and ranges and filters. Only after these foundation topics

are covered will I continue to the more interesting things that you can do with a ClientDataSet, such

as creating nested datasets, cloning cursors, defining aggregate fields, and more.

For those of you who are already well versed in working with datasets, you will only need to quickly

skim through this article to see if there is something that you find interesting. If you are fairly new to

dataset programming, however, this article will provide you with essential information on the use of 

ClientDataSets. As an added benefit, most of these techniques are appropriate for any other datasets

that you may have a chance to use.

While this article focuses primarily on the use of code to navigate and edit data in a ClientDataSet, a

natural place to begin this discussion is with Delphi data-aware controls and the navigation and editing

features they provide.

Navigating with Data-Aware Controls

There are two classes of controls that provide data navigation. The first class is navigation-specific

controls. Delphi provides you with one control in this category, the DBNavigator.

The DBNavigator, shown in the following image, provides a VCR-like interface for navigating data and

managing records. Record navigation is provided by the First, Next, Prior, and Last buttons. Record

management is provided by the Edit, Post, Cancel, Delete, Insert, and Refresh buttons. You can control

which buttons are displayed by a DBNavigator through its VisibleButtons property. For example, if you

are using the DBNavigator in conjunction with a ClientDataSet that reads and writes its data from a

local file (Borland calls this technology MyBase), you will want to remove the nbRefresh flag from the

VisibleButtons property, since attempting to Refresh a ClientDataSet that uses MyBase raises anexception.

Another DBNavigator property whose default value you may want to change is ShowHint. Some users

have difficulty interpreting the glyphs on the DBNavigator's buttons. For those users, setting ShowHint

to True supplements the glyphs with popup help hints. You can control the text of these hints by

editing the Hints property.

The second category of controls that provide navigation is the multi-record controls. Delphi includes

two: the DBGrid and DBCtrlGrid. A DBGrid displays data in a row/column format. By default, all fields

of the ClientDataSet are displayed in the DBGrid. You can control which fields are displayed, as well as

specific column characteristics, such as color, by editing the DBGrid's Columns collection property. Thefollowing is an example of a DBGrid.

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 2: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 2/9

A DBCtrlGrid, by comparison, is a limited, multi-record container. It is limited in that it can only hold

certain Delphi components, including Labels, DBEdits, DBLabels, DBMemos, DBImages,

DBComboBoxes, DBCheckBoxes, DBLookupComboBoxes, and DBCharts. It is also limited in that it is

not available in Kylix. As a result, the DBCtrlGrid is little used. An example of a two-row, one-column

DBCtrlGrid is shown in the following figure.

Depending on which multi-record control you are using, you can navigate between records using

UpArrow, DownArrow, Tab, Ctrl-End, Ctrl-Home, PgDn, PgUp, among others. These key presses may

produce the same effect as clicking the Next, Prior, Last, First, and so on, buttons in a DBNavigator. It

is also possible to navigate the records of a dataset using the vertical scrollbar of these controls.

How you edit a record using these controls also depends on which type of control you are using, aswell as their properties. Using the default properties of these controls, you can typically press F2 or

click twice on a field in one of these controls to begin editing. Posting a record occurs when you

navigate off an edited record. Inserting and deleting records, depending on the control's property

settings, can also be achieved using Ins and Ctrl-Del, respectively. Other operations, such as Refresh,

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 3: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 3/9

are not directly supported. Consequently, in most cases, multi-record controls are combined with a

DBNavigator to provide a complete set of record management options.

Detecting Changes to Record State

Changes that occur when a user navigates or manages a record using a data-aware control is

something that you may want to get involved with, programmatically. For those situations, there are a

variety of event handlers that you can use to evaluate what a user is doing, and provide a customized

response. ClientDataSets, as well as all other TDataSet descendents, posses the following event

handlers: AfterCancel, AfterClose, AfterDelete, AfterEdit, AfterInsert, AfterOpen, AfterPost,AfterRefresh, AfterScroll, BeforeCancel, BeforeClose, BeforeDelete, BeforeEdit, BeforeInsert,

BeforeOpen, BeforePost, BeforeRefresh, BeforeScroll, OnCalcFields, OnDeleteError, OnEditError,

OnFilterRecord, OnNewRecord, and OnPostError.

There are additional event handlers that are available in most situations where a ClientDataSet is

being navigated and edited, and which are always available when data-aware controls are concerned.

These are the event handlers associated with a DataSource. Since all data-aware controls must be

connected to at least one DataSource, the event handlers of a DataSource provide you with another

source of customization when a user navigates and edits records. These event handlers are

OnDataChange, OnStateChange, and OnUpdateData.

OnDataChange triggers whenever a ClientDataSet arrives at a new records, as well as when a

ClientDataSet arrives at the first record when it is initially opened. OnStateChange triggers when aClientDataSet changes between state, such as when it changes from dsBrowse to dsEdit (when a user

enters the edit mode), or when it changes from dsEdit to dsBrowse (following the posting or

cancellation of a change). Finally, OnUpdateData triggers when the dataset to which the DataSource

points is posting its data.

Navigating Programmatically

Whether data-aware controls are involved or not, it is sometimes necessary to use code to navigate

and edit data in a ClientDataSet, or any DataSet descendent for that matter. For a ClientDataSet,

these core navigation methods include First, Next, Prior, Last, MoveBy, and RecNo. The use of First,

Next, Prior, and Last are pretty much self-explanatory. Each one produces an effect similar to the

corresponding buttons on a DBNavigator.MoveBy permits you to move forward and backward in a ClientDataSet, relative to the current record.

For example, the following statement moves the current cursor 5 records forward in the dataset (if 

possible):

ClientDataSet1.MoveBy(5);

To move backwards in a dataset, pass MoveBy a negative number. For example, the following

statement will move the cursor to the record that is 100 records prior to the current records (again, if 

possible):

ClientDataSet1.MoveBy(-100);

The use of RecNo to navigate might come as a surprise. This property, which is always returns -1 in

the TDataSet class, can be used for two purposes. You can read this property to learn the position of 

the current record in the current record order (based on which index is currently selected). In the

ClientDataSet you can also write to this property. Doing so moves the cursor to the record in the

position defined by the value you assign to this property. For example, the following statement will

move the cursor to the record in the 5th position of the current index order (if possible):

ClientDataSet1.RecNo := 5;

Each of the preceding examples has been qualified by the statement that the operation will succeed if 

possible. This qualification has two aspects to it. First, the cursor movement will not take place if the

current record has been edited, but cannot be posted. For example, if data that cannot pass at least

one the ClientDataSet's Contraints has been added to a record. When you attempt to navigate off a

record that cannot be posted, an exception is raised.

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 4: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 4/9

The second situation where the record navigation might not be possible is related to the current record

position and the number of records in the dataset. For example, if the current record is the last in the

dataset, it makes no sense to move 5 records forward. Similarly, if the current record is the 99th in

the dataset, an attempt to move backwards by 100 records will fail. You can determine whether an

attempt to navigate succeeded or failed by reading the Eof and Bof properties of the ClientDataSet.

Eof (end-of-file) will return True if a navigation method attempted to move beyond the end of the

table. When Eof returns True, the current record is the last record in the dataset.

Similarly, Bof will return True if a backwards navigation attempted to move before the beginning of 

the dataset. In that situation the current record is the first record in the dataset.

RecNo behaves differently. Attempting to set RecNo to a record beyond the end of the table, or prior

to the beginning of the table, raises an exception.

Scanning a ClientDataSet

Combining several of the methods and properties described so far provides you with a mechanism for

scanning a ClientDataSet. Scanning simply means the systematic navigation from one record to the

next, until all records in the dataset have been visited. The following code segment demonstrates how

to scan a ClientDataSet.

 procedure TForm1.Button1Click(Sender:

TObject);

 begin

if not ClientDataSet1.Active then ClientDataSet1.Open;

ClientDataSet1.First;

while not ClientDataSet1.EOF do

 begin

  //perform some operation based on one or 

  //more fields of the ClientDataSet

ClientDataSet1.Next;

end ;

end ;

Editing a ClientDataSetYou edit a current record in a ClientDataSet by calling its Edit method, after which you change the

values of one or more of its fields. Once your changes have been made, you can either move off the

record to attempt to post the new values, or you can explicitly call the ClientDataSet's Post method. In

most cases, navigating off the record and calling Post produce the same effect. But there are two

instances where they do not, and it is due to these situations that an explicit call to Post should be

considered essential. In the first instance, if you are editing the last record in a dataset and then call

Next or Last, the edited record is not posted. The second situation is similar, and involves editing the

first record in a dataset followed by a call to either Prior to First. So long as you always call Post prior

to attempting to navigate, you can be assured that your edited record will be posted (or raise an

exception due to a posting failure).

If you modify a record, and then decide not to post the change, or discover that you cannot post thechange, you can cancel all changes to the record by calling the ClientDataSet's Cancel method. For

example, if you change a record, and then find that calling Post raises an exception, you can call

Cancel to cancel the changes and return the dataset to the dsBrowse state.

To insert and post a record you have several options. You can call Insert or Append, after which your

cursor will be on a newly inserted record (assuming that you started from the dsBrowse state. If you

were editing a record prior to calling Insert or Append, a new record will not be inserted if the record

being edited can not be posted). Once it is inserted, assign data to the fields or that record and call

Post to post those changes.

The alternative to calling Insert or Append is to call InsertRecord or AppendRecord. These methods

insert a new record, assign data to one or more fields, and attempt to post, all in a single call. The

following is the syntax of the InsertRecord method. The syntax of AppendRecord is identical.

procedure InsertRecord(const Values: array of const);

You include in the constant array the data values you want to assign to each field in the dataset. If 

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 5: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 5/9

you want to leave particular field unassigned, include the value null in the variant array. Fields you

want to leave unassigned at the end of the record can be omitted from the constant array. For

example, If you are inserting and posting a new record into a four-field ClientDataSet, and you want

to assign the first field the value 1000 (a field associated with a unique index), leave the second and

fourth fields unassigned, but assign a value of 'new' to the third record, your InsertRecord invocation

may look something like this:

ClientDataSet1.InsertRecord([1001, null,

'new']);

The following code segment demonstrates another instance of record scanning, this time with edits

that need to be posted to each record. In this example, Edit and Post are performed within try blocks.

If the record was placed in the edit mode (which corresponds to the dsEdit state), and cannot be

posted, the change is canceled. If the record cannot even be placed into edit state (which for a

ClientDataSet should only happen if the dataset has its ReadOnly property set to True), the attempt to

post changes is skipped.

 procedure TForm1.Button1Click(Sender:

TObject); begin

if not ClientDataSet1.Active then ClientDataSet1.Open;

ClientDataSet1.First;

while not ClientDataSet1.EOF do

 begin

  try

ClientDataSet1.Edit;

  try

ClientDataSet1.Fields[0].Value :=

UpperCase(ClientDataSet1.Fields[0].Value);

ClientDataSet1.Post;

  except

  //record cannot be posted. Cancel;

ClientDataSet1.Cancel;

  end ;  except

  //Record cannot be edited. Skip

  end ;

ClientDataSet1.Next;

end ; //while

end ;

Note: Rather than simply canceling changes that cannot be posted, an alternative except clause would

identify why the record could not post, and produce a log which can be used to apply the change at a

later date. Also note that if these changes are being cached, for update in a subsequent call to

ApplyUpdates, the ClientDataSet provides an OnReconcileError event handler that can be used to

process failed postings.

Disabling Controls While Navigating

If the ClientDataSet that you are navigating programmatically is attached to data-aware controls

through a DataSource, and you take no other precautions, the data-aware controls will be affected by

the navigation. In the simplest case, where you move directly to another record, the update is

welcome, causing the controls to repaint with the data of the newly arrived at record. However, when

your navigation involves moving to two or more records in rapid succession, such as is the case when

you scan a ClientDataSet, the updates can have severe results.

There are two reasons for this. First, the flicker caused by the data-aware controls repainting as the

ClientDataSet arrives at each record is distracting. More importantly, however, is the overhead

associated with a repaint. Repainting visual controls is one of the slowest processes in most GUI

(graphic user interface) applications. If your navigation involves visiting many records, as often thecase when you are scanning, the repaints of your data-aware controls represents a massive amount of 

unnecessary overhead.

To prevent your data-aware controls from repainting when you need to programmatically change the

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 6: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 6/9

Page 7: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 7/9

Each of the Buttons on this form is associated with an event handler that performs the indicated type

of navigation. In addition, this project includes OnDataChange and OnStateChange DataSource event

handlers that are used to update the panels in the StatusBar at the bottom of the form. These event

handlers are shown in the following code listing.

 procedure TForm1.SelectDataFile;

 begin

if OpenDialog1.Execute then

   begin

  if ClientDataSet1.Active then ClientDataSet1.Close;

ClientDataSet1.FileName := OpenDialog1.FileName;

ClientDataSet1.Open;

  end 

  else

Halt;

end ;

 procedure TForm1.FormCreate(Sender: TObject);

 begin

SelectDataFile;

end ;

 procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

 begin

StatusBar1.Panels[0].Text := 'Record ' +

IntToStr(ClientDataSet1.RecNo) + ' of ' +

IntToStr(ClientDataSet1.RecordCount);

StatusBar1.Panels[2].Text :=

  'BOF = ' + BoolToStr(ClientDataSet1.Bof, True) +

'. ' +  'EOF = ' + BoolToStr(ClientDataSet1.Eof, True) +

'. ';

end ;

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 8: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 8/9

 procedure TForm1.DataSource1StateChange(Sender: TObject);

 begin

StatusBar1.Panels[1].Text :=

  'State = ' + GetEnumName(TypeInfo(TDataSetState),

Ord(ClientDataSet1.State));

end ;

 procedure TForm1.FirstBtnClick(Sender: TObject);

 begin

ClientDataSet1.First;

end ;

 procedure TForm1.NextBtnClick(Sender: TObject);

 begin

ClientDataSet1.Next;

end ;

 procedure TForm1.PriorBtnClick(Sender: TObject);

 begin

ClientDataSet1.Prior;

end ;

 procedure TForm1.LastBtnClick(Sender: TObject);

 begin

ClientDataSet1.Last;

end ;

 procedure TForm1.ScanForwardBtnClick(Sender: TObject);

 begin

if ControlsStateBtnGrp.ItemIndex = 1 then

ClientDataSet1.DisableControls;

  try

ClientDataSet1.First;

  while not ClientDataSet1.Eof do

   begin

  //do something with a record 

ClientDataSet1.Next;

  end ;

  finally

  if ControlsStateBtnGrp.ItemIndex = 1 thenClientDataSet1.EnableControls;

  end ;

end ;

 procedure TForm1.ScanBackwardBtnClick(Sender: TObject);

 begin

if ControlsStateBtnGrp.ItemIndex = 1 then

ClientDataSet1.DisableControls;

  try

ClientDataSet1.Last;

  while not ClientDataSet1.Bof do

   begin

  //do something with a record 

ClientDataSet1.Prior;

  end ;

  finally

  if ControlsStateBtnGrp.ItemIndex = 1 then

ClientDataSet1.EnableControls;

  end ;

end ;

 procedure TForm1.MoveByBtnClick(Sender: TObject);

 begin

ClientDataSet1.MoveBy(UpDown1.Position);

end ;

 procedure TForm1.RecNoBtnClick(Sender: TObject);

 begin

ClientDataSet1.RecNo := UpDown2.Position;end ;

 procedure TForm1.Open1Click(Sender: TObject);

 begin

SelectDataFile;

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122

9 20.11.2008 17:20

Page 9: Client Data Set in Detail5

8/14/2019 Client Data Set in Detail5

http://slidepdf.com/reader/full/client-data-set-in-detail5 9/9

end ;

 procedure TForm1.Close1Click(Sender: TObject);

 begin

ClientDataSet1.Close;

end ;

About the Author

Cary Jensen is President of Jensen Data Systems, Inc., a Texas-based training and consulting company

that won the 2002 Delphi Informant Magazine Readers Choice award for Best Training. He is theauthor and presenter for Delphi Developer Days (www.DelphiDeveloperDays.com), an information-

packed Delphi (TM) seminar series that tours North America and Europe. Cary is also an award-

winning, best-selling co-author of eighteen books, including Building Kylix Applications (2001,

Osborne/McGraw-Hill), Oracle JDeveloper (1999, Oracle Press), JBuilder Essentials (1998,

Osborne/McGraw-Hill), and Delphi In Depth (1996, Osborne/McGraw-Hill). For information about

onsite training and consulting you can contact Cary at [email protected], or visit his

Web site at www.JensenDataSystems.com.

Click here for a listing of upcoming seminars, workshops, and conferences where Cary Jensen is

presenting.

Copyright ) 2002 Cary Jensen, Jensen Data Systems, Inc.

ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT THE

EXPRESS, WRITTEN CONSENT OF THE AUTHOR.

Published on: 9/27/2002 12:00:00 AM

Server Response from: BDN9A

Copyright© 1994 - 2008 Embarcadero Technologies, Inc. All rights reserved.

igating and Editing a ClientDataSet http://dn.codegear.com/print/29122