How to force a Client DataSet to recalculate calculated and internal calculated fields?
Asked Answered
M

2

10

I have a ClientDatSet with a few fkInternalCalc fields. The CDS is not linked to any provider; instead it's filled on the fly. How can I force CDS to recalculate all the "calculable" fields? I can not call Refresh() because there is no provider to refresh data from. The only way I have come with so far has been to navigate through all records, which is not the best way.

PS: I have read this question and this post, but I'm hoping for a more elegant way.

Maniemanifest answered 28/7, 2013 at 10:55 Comment(8)
So, closing - opening the data set doesn't work?Longobard
@SertacAkyuz I haven't tried it yet. Doesn't closing a client dataset clear all the data in it?Maniemanifest
@SertacAkyuz I was afraid so... What does Resync() do? Any ideas?Maniemanifest
@Iman: Would you please explain why you need to force the fields calculation? It happens automaticaly everytime a field changes in response to an internal dataset event named deFieldChanged. There is a protected method named TDataset.CalculateFields that fires the process to the current record, but it is not intented to be used by any code other than the TDataset's (or a subclass of it). All this because, theoretically, a programmer wouldn't need to fire the calculation process him/herself.Feast
@AlexSC, that won't work for fkInternalCalc fields.Jameljamerson
@iMan - It fetches data from the database if I'm not mistaken. The only method I was able to figure out which does not fetch data, is the one I had mentioned in a comment to the answer of the question that you linked. You've most probably already seen it.Longobard
@Uwe: the code for calculating InternalCalc fields (RefreshInternalCalcField) is called by the same method that calls CalculateFields. It's an inner procedure declared in the scope of DataEvent. This is all true in Deelhi XE3, at least. So, for that particular version of Dephi, I believe it applies for both Calculated fields and InternalCalc fields.Feast
@Feast I need to force fields calculation because the calculated fields need to reflect changes in external events.Maniemanifest
J
8

I achieve that with a helper (stripped here to the necessary), which allows to call the protected methods without any hack. Make sure to check for DataSet.State = dsInternalCalc inside OnCalcFields for fkInternalCalc fields.

type
  TClientDataSetHelper = class helper for TClientDataSet
  public
    function AssureEditing: Boolean;
    procedure InternalCalc;
  end;

function TClientDataSetHelper.AssureEditing: Boolean;
begin
  result := not (State in [dsEdit, dsInsert]);
  if result then
    Edit;
end;

procedure TClientDataSetHelper.InternalCalc;
var
  needsPost: Boolean;
  saveState: TDataSetState;
begin
  needsPost := AssureEditing;
  saveState := setTempState(dsInternalCalc);
  try
    RefreshInternalCalcFields(ActiveBuffer);
  finally
    RestoreState(saveState);
  end;
  if needsPost then
    Post;
end;

This can easily be expanded for normal calculated fields using CalculateFields. Although this shouldn't be necessary as calculated fields are recalculated whenever any other data field changes.

Jameljamerson answered 28/7, 2013 at 13:18 Comment(3)
I tried this and get "[dcc32 Error] dMyDataModule.pas(151): E2389 Protected member 'TDataSet.RefreshInternalCalcFields' is inaccessible here".Melessa
@GolezTrol, looks like a bug. Works in XE3, but not in XE7 and XE8 (currently cannot check for XE4 to XE6). Other protected members are perfectly accessible: "setTempState", "RestoreState". Just reported as RSP-11337.Jameljamerson
To narrow it down, I was on XE5. Thanks for testing! I've solved it now using the more classic class helper: typecasting to a locally defined THackClientDataSet = class(TClientDataSet).Melessa
S
-1

This is a bit of a hack, but it works!

DBGrid.Height := 30; 
DBGrid.Height := 200; // Refresh all Rows after first
CalculatedProc(DataSet); // Refresh first calculated fields. (Write name of your calculate procedure)
Shandashandee answered 17/9, 2017 at 11:3 Comment(9)
1. Used the Delphi default function 2. Shot 3. SimpleShandashandee
4. Faster then use change states!Shandashandee
would you mind editing the answer and elaborating a bit here ?Kravits
Why don't you call "refresh" if you want to refresh all rows?Longobard
Refresh reload data from server.Shandashandee
Gilles, sorry. Can you tell me what you want some more? You want for I renamed "CalculatedProc" to "InternalCalc"? I think this is wery simple code for everyone. One can adapted to same tasks.Shandashandee
Where do you think the record buffer is retrieved from when you increase grid's height?Longobard
When the window of grid is redrawn, the calculated fields are recalculated. But it is worked, when data of buffer show in the grid.Shandashandee
No, data of buffer is not kept magically at some place when you contract the number of rows shown in the grid. When you later increase visible number of rows, data is retrieved from server to fill the necessary record buffer.Longobard

© 2022 - 2024 — McMap. All rights reserved.