After adding some constraints and field properties to the server, let's return our attention to the client application. The first version was very simple, but now you can add several features to make it work well. In the ThinCli2 example, I've embedded support for checking the record status and accessing the delta information (the updates to be sent back to the server), using some of the ClientDataSet techniques already discussed in Chapter 13. The program also handles reconcile errors and supports the briefcase model.
Keep in mind that while you're using this client to edit the data locally, you'll be reminded of any failure to match the business rules of the application, which are set up on the server side using constraints. The server will also provide you with a default value for the Salary field of a new record and pass along the value of its DisplayFormat property. In Figure 16.3, you can see one of the error messages this client application can display, which it receives from the server. This message is displayed when you're editing the data locally, not when you send it back to the server.
This client program includes a button to apply the updates to the server and a standard reconcile dialog. Here is a summary of the complete sequence of operations related to an update request and the possible error events:
You can obtain an updated version of the data, which other users might have modified, by calling the Refresh method of the ClientDataSet. However, this operation can be done only if there are no pending update operations in the cache, because calling Refresh raises an exception when the change log is not empty:
if cds.ChangeCount = 0 then cds.Refresh;
If only some records have been changed, you can refresh the others by calling RefreshRecords. This method refreshes only the current record, but it should be used only if the user hasn't modified the current record. In this case, RefreshRecords leaves the unapplied changes in the change log. As an example, you can refresh a record every time it becomes the active one, unless it has been modified and the changes have not yet been posted to the server:
procedure TForm1.cdsAfterScroll(DataSet: TDataSet); begin if cds.UpdateStatus = usUnModified then cds.RefreshRecord; end;
When the data is subject to frequent changes by many users and each user should see changes right away, you should apply any change immediately in the AfterPost and AfterDelete methods, and call RefreshRecords for the active record (as shown previously) or each of the records visible inside a grid. This code is part of the ClientRefresh example, connected to the AppServ2 server. For debugging purposes, the program also logs the EMP_NO field for each record it refreshes, as you can see in Figure 16.4.
Figure 16.4: The form of the ClientRefresh example, which automatically refreshes the active record and allows more extensive updates by clicking the buttons
I've done this by adding a button to the ClientRefresh example. The handler of this button moves from the current record to the first visible record of the grid and then to the last visible record. This is accomplished by noting that there are RowCount - 1 rows visible, assuming that the first row is the fixed one hosting the field names. The program doesn't call RefreshRecord every time, because each movement will trigger an AfterScroll event with the code shown previously. The following code refreshes the visible rows, which might be triggered by a timer:
// protected access hack type TMyGrid = class (TDBGrid) end; procedure TForm1.Button2Click(Sender: TObject); var i: Integer; bm: TBookmarkStr; begin // refresh visible rows cds.DisableControls; // start with the current row i := TMyGrid (DbGrid1).Row; bm := cds.Bookmark; try // get back to the first visible record while i > 1 do begin cds.Prior; Dec (i); end; // return to the current record i := TMyGrid(DbGrid1).Row; cds.Bookmark := bm; // go ahead until the grid is complete while i < TMyGrid(DbGrid1).RowCount do begin cds.Next; Inc (i); end; finally // set back everything and refresh cds.Bookmark := bm; cds.EnableControls; end; end;
This approach generates a huge amount of network traffic, so you might want to trigger updates only when there are actual changes. You can implement this process by adding a callback technology to the server, so that it can inform all connected clients that a given record has changed. The client can determine whether it is interested in the change and eventually trigger the update request.
|Copyright © 2004-2017 "Delphi Sources". Delphi Programming Guide||