Dbgrid - automatic post to database - delphi

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.

Related

With delphi, i have a dbgrid. I want when user has filled the row and posted it, the line of grid is readonly

With delphi, i have a dbgrid. I want when user has filled the row and posted it, the line of grid is readonly
Do you have any suggestion ?
thank you
Long time since last Delphi dev but I would have aborted an OnBeforeEdit event if there is data in the buffer.
My two cents
I suggest to keep a list with all rows (All data or just the primary key or the row ID) already updated by the user. Before allowing an update, you check the list.
To prevent accidental / unwanted edits you can turn off the AutoEdit property of the TDataSource providing data to the DBGrid. Then be sure any controls like a DBNavigator do not have edit enabled.
From the Delphi Help:
Description
Specifies whether the data source should call the Edit method automatically.
AutoEdit is true by default. If AutoEdit is true, then when a user attempts to modify the data displayed by the control, the data source calls the underlying dataset's Edit method.
Set AutoEdit to false to protect data from unintentional modification. Even if AutoEdit is false, an application can explicitly call a dataset's Edit method to allow data modification.

responding to dbCheckbox changes programmatically

I have a no of DBCheckboxes on a form I want to be able to check/uncheck one or more of these programmatically depending upon whether the user checks /unchecks a different DBCheckbox also on the same form I cant seem to be able to do this using the onclick event
I can't seem to be able to do this using the onclick event
The reason is the tight coupling between the state of db-aware components such as TDBCheckBox and that of the dataset they are connected to. If you attempt to interfere with this by trying to set the gui state of the component (e.g. the Checked state of a DBCheckBox), the db-aware model these components all work to will fight you every inch of the way, because you are effectively trying to subvert the mechanism by which the gui state of the component is kept in sync with the value in the corresponding dataset field.
So, as Val Marinov correctly said, the thing you need to do is to manipulate the field value instead, as in the following:
if MyDataSet.FieldByName('OtherBooleanField').AsBoolean <> RequirdValue then begin
if not (MyDataSet.State.State in [dsInsert, dsEdit]) then
MyDataSet.Edit;
MyDataSet.FieldByName('OtherBooleanField').AsBoolean := RequiredValue;
It's up to you what trigger you respond to, to execute code like this.
on clickevent of check box set the Field of appropriate checkbox
In the OnChange event of the fields change the values of the other fields that need to change.

Cannot assign to a read only property

I have a form which has a TDBLookupComboBox on it.
The TDBLookupComboBox is displaying a list of records from within a database table.
At the point of the forms OnShow event I would like the TDBLookupComboBox to already display one of the strings in the list.
I have done this so far...
procedure TfrmMain.FormShow(Sender: TObject);
begin
dblucbox.Text := Username;
end
Username is a string for one of the records already in the list.
At the point of compiling, I get an error saying
Cannot assign to a read only property
I'm a bit stuck with this so any help would be appreciated.
Don't try to modify the Text property, instead if you want set the TDbLookUpComboBox in a particular item you must use the the KeyValue property which will try to locate the record in the underlying TDataSet.
So you if you have Key Value of the user you can use something like this
dblucbox.KeyValue := UserId;
Otherwise you can use the Locate method of the underlying TDataSet, to find the match and the LookUp control will be refreshed automatically
You're going about this backwards. To be more precise, the DBLookupCombo is reflecting the state of the database table. So you want to be manipulating the table, not the combobox.
In other words, the OnShow event needs to open the table that's the object of the DBLookupCombo (if it's not already open) and then position the current record to be the one you want displayed as the default.

How I can get the changes of a TClientDataset to another TClientDataset?

How can I get from a TClientDataset the changes?
I have a TClientDataset named GetDataset and I have a grid. I want the changes in a new TClientDataset named ChangeDataset.
How can I do this?
If you have a source ClientDataSet CDS1, you should be able to copy the changed records to a second ClientDataSet CDS2 by doing
if CDS1.ChangeCount > 0 then
CDS2.Data := CDS1.Delta;
As you'll see if you try that, it gives you a "before" record and one with the change(s). That may not necessarily be what you want - frankly you'd do best to read the Whipple article posted in a comment and in the OLH to get the exact result you might be wanting to achieve. The point is, all the information you need is in the source CDS until you flush it away (by calling ApplyUpdates() - after that, if it succeeds, the change log is empty).
If you look at the rows in CDS2, it's not immediately obvious how you tell whether a particular field contains a changed value and how you distinguish one that does from one which is just empty. Istr there was a very good post quite a long time ago in one of the Borland NTTP newsgroups by their Mark Edington, I think, explaining how to do this. Basically, it's a question of evaluating VarIsClear on the field's NewValue property:
if VarIsClear(CDS2.Fields[i].NewValue) then
// means Fields[i] does not have a changed value
Incidentally, since you can save the state of a CDS to XML, you can use XML manipulations, e.g. with a DOM parser such is the Windows built-in one (see MSXML.Pas) to do easily many things which are troublesome to do using the TDataSet paradigm.

running Procedure by Trigger on new data in OracleDB

I wrote a procedure to update a table by deriving its data from four other tables. The procedure itself works fine, but I need the table to be up to date all the time, so I call the procedure from triggers which fire, whenever one of the four tables ist altered.
This too works fine except for one detail. The procedure derives the data from the tables BEFORE the changes, so the update/delete/insert, which fired the trigger.
The trigger is rowlevel after u/d/i but even changing it to after statement did not help.
Not using a trigger is not really an option, because of the need being in sync. Changing it to a materialized view seems to be a good idea, but neede calculations are disallowed in those and nonmaterialized views are too slow, which is why I want to change it from a nonmaterialized view to a table.
Handing all information about what was changed in which way over to the procedure would be possible, but it would make the procedure so much more complicated, that I refrain from writing this code.
Is there any way to derive my table based on the new values directly triggered by a change in the four tables?
Regards
Ramin

Resources