Subject Re: [IBO] Calculate Fields
Author Helen Borrie
At 04:49 PM 30/06/2005 -0300, you wrote:
>As Helen asked, I'm posting here my question about the subject:
>I need to calculate a total at a sales table, at client.
>I look at Helen example (, but it doesn't calculate
>while qrSales.State = dssInsert.
>I found at messages repository, some messages about this, and one of
>them, says to call OnCalculateField event when it is needed, so, I can
>call it at edit OnExit event that receive qty and price values, but
>I'm having difficulty on doing that. Where I get the ARow and AField
>I have configured qrSales like that:
>qrSales.CalculatedFields:=TTOTAL Numeric(12,2)

No. CalculatedFields is a stringlist:

qrSales.CalculatedFields.Add('TTOTAL Numeric(12,2)');

But it is simpler - and easier for maintaining - if you plug the definition
directly into the dataset in the IDE, via the CalculatedFields editor! It
will then be created when the dataset is created and destroyed when the
dataset is destroyed.

TTOTAL Numeric(12,2)

>procedure TDM.qrSalesCalculateField(Sender: TIB_Statement;
> ARow: TIB_Row; AField: TIB_Column);
>if AField.FieldName = 'TTOTAL' then
> with ARow do
> begin
> ByName('TTOTAL').AsCurrency :=
> ByName('QTY').AsCurrency*
> ByName('PRICE').AsCurrency;
> end;

The CalculateField method operates on the entire row and needs to calculate
the values for every calculated field defined for the set. In your case,
there is only one and your logic (with the calc field reference changed to
AField) will work. It is good style to code your CalculateField procedure
as though you expect more calc fields:

procedure TDM.qrSalesCalculateField(Sender: TIB_Statement;
ARow: TIB_Row; AField: TIB_Column);
with ARow do
if AField.FieldName = 'TTOTAL' then
ByName(AField).AsCurrency :=
// else if AField.FieldName = 'XYZ' then
// begin
// ...
// end

(The commented stuff is just for demonstration - of course you won't
include something that isn't there!)

IBO will call CalculateFields whenever it knows that the value(s) of the
calculated fields are invalid, i.e. during an Open, Fetch or Refresh, or
when an event occurs that it knows has the potential to invalidate the
calculated fields' values. You can call it yourself at other times, in the
handler for an event that the dataset might not otherwise detect as an
invalidating one. In such a handler, you can refer to the array OldValues[]
- another TIB_Row object that the dataset maintains. Look up the IBO Help
for other Old.. properties that might be useful here.

It is important to realise that a UI control that is accepting input is
just a container for characters. Anything happening inside a UI field
control (grid cell or an edit control) does not update the datasource until
focus shifts out of that UI field (triggering OnDataChange in the
datasource) or a row change event occurs. That means that the new value is
not delivered to the dataset until this event has occurred. An EditMask
will step in during an input operation and block entry of invalid
characters. Otherwise, validation is deferred until the input event is
signalled as finished by the focus shift or change event. Following that
event, or as an end action, you can make an ad hoc call to CalculateFields
if you need to.