13

I want to create a 'fake' data field in a DataSet (not ClientDataSet):

  • the field should not be stored in the db
  • it's not a calculated field (the user should be allowed to enter input data)
  • the field has business logic meaning, so after the user updates its value it should update other fields (with the OnFieldChange event)

I know I can have a simple no-dbaware control, capture it's OnChange event and perform the calculations there (or call a DataModule function, where the DataSet resides) but I think it's more clean if I can reutilize the dataset automatic binding with db-ware controls and dataset events..

Also this way the unique connection between the Form (Presentation) and the DataModule (Model) it's the DataSet (less coupling)..

PD: I'm using fibplus, with I think the solution (if any) will be at the VCL level..

Thanks!

pragmatic_programmer
  • 3,566
  • 3
  • 29
  • 36
  • Using DB Aware controls for non-db stuff is really sub-optimal. There are other ways to avoid connecting things you don't want to connect. – Warren P Nov 03 '11 at 19:58
  • It is DB data, just it does not get stored in this particular table.. Also I know about bindings, but I'm using D2010 and I don't have time to start with a custom library.. (http://stackoverflow.com/questions/7882599/it-is-possible-to-do-something-like-xe2-livebindings-in-d2010) – pragmatic_programmer Nov 03 '11 at 21:04

3 Answers3

10

Have you tried using an InternalCalc field? Your data aware controls will let you edit an InternalCalc field's value, and the value is stored in the dataset.

If you create an InternalCalc field in the dataset (TClientDataSet, TQuery, etc.) at design time, it's almost exactly what you're asking for.

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
  • I was just going to write the same answer. – Uwe Raabe Nov 03 '11 at 17:27
  • This looks like the native vcl way to go.. I tested it and works great at design time.. unfortunately I create all my datasets at runtime without fielddef definitions.. (basically I set the SelectSQL.Text, and call Open, so the fields get created from the SQL field list). I tried to add the field in the AfterOpen event of the DataSet but it raises an excepcion "cannot perfom this operation on an opened dataset".. any workaround to solve this? thanks – pragmatic_programmer Nov 03 '11 at 20:57
  • This is not working with the auto-create-field option. It is the same with normal calc fields. You have to either use static fields or create all fields at runtime before the dataset is opened. It might be possible to open the dataset, save the field defs, close it, create the fields plus the calc fields and open it again. This will need some tricky implementation though. – Uwe Raabe Nov 03 '11 at 21:17
3

You could create an updatable view (with before insert/update/delete triggers) in your Interbase/Firebird database. This would look like a "virtual table" to the client (the select statement of the view's declaration can also contain "virtual fields") and it's totally up to you how you implement the triggers.

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • A nice trick for other ocasions I believe.. in this case 1) the logic goes in the triggers 2) I have to post to the virtual table (updatable view) to get the trigger executed and get the other fields updated (this last thing is not viable in my case).. I'm right? – pragmatic_programmer Nov 03 '11 at 21:02
  • Yes, the client would have to post to the view and reload the view to get updated values. – Ondrej Kelle Nov 04 '11 at 08:44
2

You can make a Calcfield writeable:

type
  TCalcStringField = class(TWideStringField)
    function GetCanModify: Boolean; override;
  end;


function TCalcStringField.GetCanModify: Boolean;
begin
  // Makes Calcfield editable
//if FieldNo > 0 then
    if DataSet.State <> dsSetKey then
      Result := not ReadOnly and DataSet.CanModify
    else
      Result := IsIndexField
//else
//  Result := False;
end;

Set Calculated to true, and in OnSetText of this Field you can then write the Text to any other place

procedure TformXX.XXOnSetText(Sender: TField; const Text: string);
begin
   ...
end;
stha68
  • 21
  • 1