Suppose I have a dataset say dsSample in my Delphi application. To read or write data in a dataset, one must open the dataset. I just wanted to know what is the difference between following statements:
dsSample.Open;
dsSample.Active := True;
and
dsSample.Close;
dsSample.Active := False;
If Open and Active perform same operation, why two different keywords for opening and closing the datasets in Delphi?
Use either as here is little difference, the DataSet.Open method has one line of code: Active := True. Active being a property that will call SetActive or GetActive. Finding out these things is fairly straight forward if you hold down CTRL and click on Open or Active and have a read of the code in the VCL source, knowing a few of the internals of the VCL will stop you doing things like:
if not DataSet.Active then
DataSet.Open;
rather than just
DataSet.Open;
As TLama indicated, Active is a published property making it available to the Delphi IDE, allowing you to toggle it at design time for your DataSets on Forms or Datamodules. Open and Close are probably not strictly required, but is a fairly common pattern across many languages.
TDataset.Open is a procedure so you can't get dataset is populated with data or not
procedure TDataSet.Open;
begin
Active := True;
end;
Active is a property :
Use Active to determine or set whether a dataset is populated with data. When Active is false, the dataset is closed; the dataset cannot read or write data and data-aware controls can not use it to fetch data or post edits. When Active is true, the dataset can be populated with data. It can read data from a database or other source (such as a provider). Depending on the CanModify property, active datasets can post changes.
Setting Active to true:
Generates a BeforeOpen event.
Sets the dataset state to dsBrowse.
Establishes a way to fetch data (typically by opening a cursor).
Generates an AfterOpen event.
If an error occurs while opening the dataset, dataset state is set to dsInactive, and any cursor is closed.
Setting Active to false:
Triggers a BeforeClose event.
Sets the State property to dsInactive.
Closes the cursor.
Triggers an AfterClose event.
An application must set Active to false before changing other properties that affect the status of a database or the controls that display data in an application.
Note: Calling the Open method sets Active to true; calling the Close method sets Active to false.
Related
I have a TClientDataSet which stores data coming from a medical instrument. This client dataset is linked to a grid to display data in real time. My problem is, when the user is editing the data, and the instrument sends a new packet, the data which the user has modified but not yet posted is lost because I only can get a TBookmark on current record, append the new record, and then goto the saved bookmark (which is sometimes not the correct record, apparently due to the new record). I can check dataset's State, Post if necessary, and then set the State afterwards, I'm looking for a way to update data in client dataset without affecting its State. Is this even possible?
Clone the dataset and modify the data on the clone.
A document on it by Cary Jensen is here: http://edn.embarcadero.com/article/29416
Basically you need something like
var
lEdDataset: TClientdataset;
begin
lEdDataset := TClientDataSet.create(nil);
try
lEdDataset.CloneCursor(SourceDataSet, True**);
StoreMedDeviceRecord(lEdDataset);
finally
lEdDataset.free;
end;
** You'll need to read the documentation on the True/False settings and decide what you actually need (I can't remember off-hand)
I have many "master/detail" forms in my application. A TDBGrid where each row shows a few core values of the item. Below the grid is usually a "Detail area" that shows the complete information of the item that is currently selected in the grid.
Currently I am listening to the "AfterScroll"-event of the TADOQuery behind the grid, but it seems to give me too many events.
Is AfterScroll the correct event for this? How are you doing this?
The "standard" way (in a data-aware environment) would be to not control that using GUI controls, but rather using the data components.
Most of the table data sets provide MasterSource (linked to an appropriate TDataSource component), and MasterFields properties.
You use these to link your datasets in a master-detail relationship.
Then your detail grid (or other data-aware controls) only needs to concern itself with linking to the correct dataset.
EDIT
Other kinds of datasets (e.g. TQuery, TADOQuery) sometimes provide DataSource to be used for a similar purpose. From Delphi 5 help: "Set DataSource to automatically fill parameters in a query with fields values from another dataset."
However, there are quite a few more complications (as will be observed reading the help). So it may be advisable to use TTable or TADOTable for the detail dataset instead.
I'm not aware if there's any 'standard' way but IMO AfterScroll is fine. Use a timer to prevent updating controls in quick succession, for instance while scrolling the grid. An example:
procedure TSomeForm.DataSetAfterScroll(DataSet: TDataSet);
begin
if not DataSet.ControlsDisabled then begin
if ScrollTimer.Enabled then
ScrollTimer.Enabled := False;
ScrollTimer.Enabled := True;
end;
end;
procedure TSomeForm.ScrollTimerTimer(Sender: TObject);
begin
ScrollTimer.Enabled := False;
UpdateGUI;
end;
I think you'll find an interval of 250-300 ms pleasant.
I have been asked by my company to update the reporting functionality of a paticular application written in delphi and using Quick reports to use FastReports instead.
The current implementation pulls all the data out of the database, does a lot of work to organise and calculate the required data for the reports and stores all this in several different objects. The Quick Report OnNeedData events are then used to fill out the bands until there is no more data (signified by setting MoreData = false)
The problem I'm having is Fast Reports seems to need a band to be connected to an actual data source, which I don't have. Also fastReports doesn't seem to have an event similar to OnNeedData.
Is there anyway to fill out the values of a data band in code and have it print again until all data is printed without connecting the band to a dataset?
I appologise for the vagueness of this question, I am very new to reporting software and any suggestions of where to go and what to look at would be greatly appreciated.
Fast reports use a intermediary object descending from _TFrxDataSet to connect the report engine which the data it prints.
To connect a report to a data source managed by the program itself, you use a TfrxUserDataSet component, which let's you see a data set inside the report, but you manually specify the column names in the Fields (TStrings) property and manage and supply values programatically writing event handlers for the following events:
OnCheckEOF is functionally equivalent to the OnNeedData, if there's no more to print, you set the EOF var parameter to true
OnFirst you do whatever you have to do to start walking for the data.
OnGetValue and OnNewGetValue you provide values for each of the different columns of the current row
OnNext, OnPrior you move your current row one next or one prior position.
As you see, the row/column concept (a DataSet) is used to provide the data to the report, but you can pull your data from any structure you use to store the result of your calculations (lists, arrays, or any other object/structure/file etc.)
Inside the report, you link the band to this logical DataSet and you use standard components to print the column values of this DataSet.
If you already have the data in a DataSet, for example a in-memory DataSet after your calculations, better use a TfrxDBDataset to directly bind your report to that source of data.
you can use TfrxUserDataSet.There is a demo named 'printstringlist' under folder 'demos'.
In our project we have implemented our own class inherited from TfrxCustomQuery. This new query class simply redirects its SQL statements to our application' internal query engine. We registered this new class in FastReport palette (used frxDsgnIntf.frxObjects.RegisterObject* for FR version 3 and 4) and now it is used in all our report templates instead of TfrxADOQuery or other built-in dataset classes.
Here is another alternative:
I've been using FastReport for years. I sometimes run into a similar situation. For offline tabular reports I use an in-memory dataset. I purchased DevExpress long time ago and so I have TdxMemData. But even without it, you should be happy using the TClientDataset component.
Besides that, the TfrxUserDataset is an alternative which I use when showing lists of objects.
There is a possibility to do it like this, it is slow though ,
Code:-
var
FRX: TfrxReport;
procedure NewPage;
begin
MyPage := TfrxReportPage.Create(FRX);
MyPage.CreateUniqueName;
MyPage.PaperSize := DMPAPER_A4;
end;
procedure ...(AText: string);
var
frMemo : TfrxMemoView;
begin
frMemo := TfrxMemoView.Create(MyPage);
frMemo.CreateUniqueName;
frMemo.Text := AText;
end;
Regards
Herman
I have a form with a query, a dataset, an editable dbgrid and an updatesql component. When I need to save the changes made in the dbgrid, I call this procedure:
procedure TEditCardDetailForm.SaveChanges;
begin
Database1.StartTransaction;
try
Query2.ApplyUpdates;
Database1.Commit;
except
Database1.Rollback;
raise;
end;
Query2.CommitUpdates;
end;
However I want the changes to be applied automatically to the database when I press Enter or go to another row after editing a cell in the dbgrid - the way it is done when I use a TTable component. Is there a way to do it?
If I understand it right (please correct me if not) you have a TQuery with CachedUpdates set to true, but want it to behave as if it would not be using cached but immediate updates. If that is the case the way you have set up your TQuery contradicts your desired behaviour. Cached updates are to be "held" on the client side until you decide to manually post them to the database by using ApplyUpdates.
In case you can set CachedUpdates to false, you only need to do following:
Link the TUpdateSQL to the TQuery via its UpdateObject property.
Write the insert, update and delete statements and assign them to the InsertSQL, ModifySQL and DeleteSQL properties of the TUpdateSQL.
I guess you already have done these two things, so putting CachedUpdates to false should do it.
You can find more information on Cached Updates for Delphi 5 here.
HTH
You have two scenarios to handle here:
save changes when changing the grid row
save changes when changing the grid column
The first one is easy to implement by calling your SaveChanges procedure in the AfterPost event of the underlying dataset (query2, a TClientDataSet?).
For the second one you only have to call query2.Post after the column has changed. This can be done in the OnDataChange event of the datasource. Make sure to check for Field <> nil and the dataset being in insert or edit mode before calling post.
I have code like below in a project I'm working.
procedure TForm.EditBtnClick(Sender:TObject);
begin
// Mark is form variable. It's private
Mark = cdsMain.GetBookmark;
// blabalbal
.
.
.
end;
procedure TForm.OkBtnClick(Sender:TObject);
var
mistakes: Integer;
begin
//Validation stuff and transaction control
//removed to not clutter the code
If cdsMain.ChangeCount <> 0 then
mistakes := cdsMain.AppyUpdates(-1);
cdsMain.Refresh;
try
cdsMain.GotoBookmark(Mark);
// Yes, I know I would have to call FreeBookmark
// but I'm just reproducing
except
cdsMain.First;
end;
end;
Personally, I do not use bookmarks much — except to reposition a dataset where I only moved the cursor position (to create a listing, fill a string list, etc). If I Refresh, update (especially when a filter can make the record invisible), refetch (Close/Open) or any operation that modifies the data in the dataset, I don't use bookmarks. I prefer to Locate on the primary key (using a TClientDataset, of course) or requery modifying the parameters.
Until when is a bookmark valid? Until a Refresh? Until a Close/Open is done to refetch data? Where does the safe zone end?
Consider in the answer I'm using TClientDataset with a TSQLQuery (DbExpress).
Like both c0rwin and skamradt already mention: the bookmark behaviour depends on the TDataSet descendant you use.
In general, bookmarks become invalid during:
close/open
refresh (on datasets that support it)
data changes (sometimes only deletions)
I know 1. and 2. can invalidate your bookmarks in TClientDataSets. I am almost sure that for TClientDataSets it does not matter which underlying provider is used (TSQLQuery, TIBQuery, etc).
The only way to make sure what works and what not is testing it.
Which means you are totally right in not using them: bookmarks have an intrinsic chance of being unreliable.
To be on the safe side, always call BookmarkValid before going to a bookmark.
Personally I rarely ever use bookmarks. I instead use the id of the record I am viewing and perform a locate on it once the refresh is complete. If I need to iterate over all of the records in the set, I do that using a clone of the tClientDataset (which gets its own cursor).
It is my understanding is that the implementation of the bookmark is up to the vendor of the tDataset descendant and can vary between implementations. In my very simple dataset (tBinData), I implemented bookmarks as the physical record number so it would persist between refreshes as long as the record was not deleted. I can not speak this true for all implementations.
TDataSet implements virtual bookmark methods. While these methods ensure that any dataset object derived from TDataSet returns a value if a bookmark method is called, the return values are merely defaults that do not keep track of the current location. Descendants of TDataSet, such as TBDEDataSet, reimplement the bookmark methods to return meaningful values as described in the following list:
BookmarkValid, for determining if a specified bookmark is in use.
CompareBookmarks, to test two bookmarks to see if they are the same.
GetBookmark, to allocate a bookmark for your current position in the dataset.
GotoBookmark, to return to a bookmark previously created by GetBookmark
FreeBookmark, to free a bookmark previously allocated by GetBookmark.
Get it from here