Refresh one row in cxGrid - delphi

I have seen there is a problem refreshing only current row if the data are changed in another form (and DataSet too) or even if the data are changed on server side (in trigger or from another user).
So i find solution that partially work with DevExpress's cxGrid and have following issue.
frm:=TfrmEdb01.Create(Self); // create edit-form
try
frm.LoadData(0); // do the insert in TAdQuery
frm.ShowModal; // Show edit form
// after closing an Edit-Form, make a new record in cxGrid
row:=tv01.dataController.InsertRecord(tv01.Controller.FocusedRecordIndex);
// for each column in grid...
for col:=0 to tv01.DataController.ItemCount-1 do
begin
// ...get its FieldName
sFld:=tv01.DataController.GetItemFieldName(col);
// and if this field exist in edit-form's Query
if frm.q01.FindField(sFld)<>nil then
//then assign this value to the newly created row in the grid.
tv01.DataController.Values[row,col]:=frm.q01.FieldByName(sFld).Value;
end;
finally
frm.Free;
end;
And realy, when I step out from the edit-form there is a New row created in the cxGrid. Problem is in that:
- if is double-clicked (to open edit-form again) then the previous record is opened. It seems to me the Data acctualy did not updated in Query object, only in GridView... maybe I should use values from Grid instead of Query object.
- if select another record (scroll them up and down), and then want to select again this new record, then it can not be focused at all. Only if I reload whole dataSet, then this new record can be selected same as any other.
Cheers!
p.s. To clearify a problem, I will provide whole test-project. There is also SQL script to create a table in the database, and AnyDac connection must be set to Your database.
There is also an image thate will/should ilustrate a problem.

You have call post to save the changes to the underlying dataset. Do it just before finally.
tv01.DataController.Post();
See the help for TcxCustomDataController.Post for more details.

Related

How to insert value (data) to a field reference by ID of the specific row (record) in the delphi clientdataset

I am trying to insert a data to a field referenced by specific ID of row (or record) in the clientdataset at runtime.
I am using delphi and here's the structure of my case — mysql database > mysqluniprovider > uniquery > dataset provider > clientdataset > datasource > dbgrid.
The data I am trying to insert is generated during runtime by another code in the same procedure. Hence, dbnavigator will not work for me here. On the otherhand, I prefer to do this at the clientdataset level and do not want to direct to sql.
I was able to find the reference id by using the clientdataset.lookup/locate/findkey. But I could not be able to direct the cursor to the cell of the same row of reference id and specific field to insert the data.
I believed there must be a code component for this type of case just like cds.lookup/locate/findkey to update data in an existing table at runtime.
I will greatly appreciate any help on this.
The fact that you are using a TDBGrid to display the data is incidental, you change the data in the clientdataset and the TDBGrid will automatically update its display of the record. What you neeed to do is to use methods of of the clientdataset to move to the record and update its field data. You can use the clientdataset's Locate method to move to the record you want, as in:
ID := 99; // the ID of the record to change
if ClientDataSet1.Locate('ID', ID, []) then begin
ClientDataSet1.Edit; // Put the CDS into dsEdit mode so you can change its field data
ClientDataSet1.FieldByName('SomeField').AsString := 'Whatever';
ClientDataSet1.Post; // save the change(s) to the record
end;
See the online help for TField for its various AsXXX methods, such as AsInteger, AsFloat, etc.

Key value for this row was changed or deleted at the data store. The local row is now deleted

Migrating 1.5 million lines of D7,BDE,Paradox to XE2,ADO,MS-SQL.
We have a TDBLookupComboBox that works fine. We provide the user with an ellipsis button so they can add or delete records from the combo box's ListSource table while the combo box is visible.
If the user clicks on the ellipsis, we let them edit the table and then we Refresh the comboboxes datasource, like this:
EditTable.ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Refresh
This worked fine in the Paradox world.
In the SQL/ADO world, if the user deletes a record from the ListSource, we get the message on the Refresh statement above:
Key value for this row was changed or deleted at the data store.
The local row is now deleted.
This occurs even if the record the user deleted was not the currently selected item in the combo box.
We don't understand why this is happening now but not in the Paradox version.
Our solution has been (after the user edits) to close and open the ListSource dataset as shown below, but this is clumsy (and we'll have to replicate in almost 100 places we do this kind of thing.)
Here's our current fix:
var
KeyBeforeUserEdit: Integer;
KeyBeforeUserEdit:= Form1.DBComboBox.KeyValue;
EditTable.ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Close;
Form1.DBComboBox1.ListSource.DataSet.Open;
if Form1.DBComboBox1.ListSource.DataSet.Locate('UniqueKey', KeyBeforeUserEdit, []) then
From1.DBComboBox1.KeyValue := KeyBeforeUserEdit;
Any alternate suggestions or explanations why this is necessary?
I can't know for sure what is going on but you may be able to simplify your migration (albeit not good practice) in the following way.
ShowModal is a virtual function so you can override it in the class EditTable belongs to (TEditTable?) providing that you have that source. Within the unit add the Form1 unit to the uses clause in the implementation section (if it is not already there) and add your override as follows
function TEditTable.ShowModal : integer;
var
KeyBeforeUserEdit: Integer;
begin
KeyBeforeUserEdit:= Form1.DBComboBox.KeyValue;
Result := inherited ShowModal; // user edits ListSource.Dataset table
Form1.DBComboBox1.ListSource.DataSet.Close;
Form1.DBComboBox1.ListSource.DataSet.Open;
if Form1.DBComboBox1.ListSource.DataSet.Locate('UniqueKey', KeyBeforeUserEdit, []) then
From1.DBComboBox1.KeyValue := KeyBeforeUserEdit;
end;
It is a bit of a kludge but may be pragmatic and save a lot of work.

DataSnap using AutoInc key and refresh current record only after insert

I've not been able to find an answer on this anywhere. Using Delphi XE7 with TClientDataSet, DataSnap & SQL Server. I need to insert a record, apply updates and then refresh that record so I can get the Id and assign it to my object. Seems pretty basic requirement, but on the contrary it is proving to be a royal pain.
I've found the obvious stuff on EDN, SO and Dr Bob:
http://edn.embarcadero.com/article/20847
DataSnap and the autoinc field
http://www.drbob42.com/examines/examinC0.htm
However these seem to focus on a "Refresh" of the TClientDataSet to re-fetches the entire table/query. Whilst this does actually resolve the Id field itself (good!), it also moves the cursor off the current record which was just inserted and so I'm not able to get the Id and assign it to my object. Also, for performance over HTTP I don't really want to refetch the entire table every time a record is inserted, if there's 10,000 records this will consume too much bandwidth and be ridiculously slow!
Consider the following code:
function TRepository<I>.Insert(const AEntity: I): I;
begin
FDataSet.DisableControls;
try
FDataSet.Insert;
AssignEntityToDataSet(AEntity); // SET'S ALL THE RELEVANT FIELDS
FDataSet.Post;
FDataSet.ApplyUpdates(-1);
FDataSet.Refresh; // <--- I tried RefreshRecord here but it cannot resolve the record
AEntity.Id := FDataSet.FieldByName('Id').AsInteger; // <----- THIS NOW POINTS TO WRONG ROW
finally
FDataSet.EnableControls;
end;
end;
Does anyone know how to achieve this? I need to be able to refresh and stay on the current record otherwise I do not know the Id of the record just created and the GUI cannot stay focused on the current record.
Hopefully something obvious I'm missing.
Cheers.
Rick.
Assuming you can get hands on the new ID inside the AfterUpdateRecord event of your DataProvider, your event handler then may look like this (the current record of DeltaDS is the one just inserted into SourceDS):
if (UpdateKind = ukInsert) then begin
DeltaDS.FindField('Id').NewValue := <TheNewID>;
end;
Make sure to have the poPropogateChanges option set in the provider. This will transfer the changed Id field back to the ClientDataSet.
Now you can get rid of the FDataSet.Refresh call.
SQL Server does allow you to get the last identity it generated in several ways - there's no need to "refresh" the record/query which means re-issuing a SELECT and can generate undesiderable side-effects. You can use SELECT SCOPE_IDENTITY() or use an OUTPUT clause. If the Delphi database driver supports it, TField.AutogenerateValue should accomplish that task automatically (see http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TField.AutoGenerateValue)
Otherwise you have to put that new data into your delta (see Raabe answer - this has to be done on the datasnap server which actually talks to the database) after reading it, so it's sent back to the client. You also need to set properly and TField.ProviderFlags to ensure data are applied correctly (see http://docwiki.embarcadero.com/RADStudio/XE7/en/Influencing_How_Updates_Are_Applied), usually you don't want those field appear in an UPDATE.

Delphi - Passing data from a TQuery to Quickreport component (QRDBText)

This may be a newbie question, but I was unable to solve this problem.
I have a TQuery component (Query1) wich returns me a dataset from a database.
I would like to pass the records in it to a QuickReport QRDBText component - with no success so far.
I am creating the Query1 in run-time, and then an event (button press) would come up with the Quickreport. I got this far with it:
QReport.DataSet:=Query1;
QRDBText1.DataSet:=Query1;
QRDBText1.Datafield:='Vnev'; //first field in query
QRDBText2.DataSet:=Query1;
QRDBText2.Datafield:='Knev'; //second field in query
QRDBText3.DataSet:=Query1;
QRDBText3.Datafield:='Idcard'; //third field in query
But this shows me the big grey nothing.
If I operate with a sample database and place a TTable on the QuickReport, and set up the connections via the Object Inspector, it works. But again: I need to display data from my Query.
Any help would come handy! Thanks
The QRdetailband should set to true and fill the QRdbtext on it.

to track the modified rows and manually update from the TClientDataSet's Delta

Is there any way to manually track the changes done to a clientdataset's delta and update the changes manually on to then db. i have dynamically created a clientdataset and with out a provider i am able to load it with a tquery, now user will do some insert update and delete operations on the data available in the cds, and at final stage these data(modified) should be post in to database by using a tquery(not apply updates)..
After populating your data set from the TQuery call MergeChangeLog so that the records do not stand out as newly inserted, and be sure that LogChanges is set.
Then when at the final stage, before updating the query with the dataset, set StatusFilter so that only the records that you want to take action on should be showing. For instance;
ClientDataSet1.StatusFilter := [usDeleted];
You can also use UpdateStatus on a record to see if it has been modified etc..
But be careful that, is seems that there will be multiple versions of a record, and it is a bit difficult to understand how the "change log" keeps track. And there also can be multiple actions on a record, like modifying it a few times and then deleting it.
Change:= TPacketDataSet.create;
Change.Data:= YourClientDataSet.Delta;
while not Change.Eof do
begin
Change.InitAltRecBuffers(False);
if Change.UpdateStatus = usUnmodified then
Change.InitAltRecBuffers(True);
case Change.UpdateStatus of
usModified: ;//your logic read codes in Provider.pas for further hint
usInserted: ;//your logic read codes in Provider.pas for further hint
usDeleted: ;//your logic read codes in Provider.pas for further hint
end;
Change.Next;
end;
Above should work regardless of number of modified
Cheers
Pham

Resources