3

I have a procedure that needs to be triggered/fired by a user deletion of data in a cell. Looks like ClientDataset.Delete is not appropriate for this case.

My data structure are:

TADOConnection -> TADOQuery -> TDataSetProvider -> TClientDataSet -> TDataSource -> TDBGRidEh (descendant of TDBGrid)

Do I need to create a procedure to detect the deletion or is there already an event or properties ready to use for this purpose?

RickyBelmont
  • 477
  • 7
  • 1
    Do you mean the deletion of a complete record or the removal of a field value (aka setting it to null)? The TDataset has OnBeforeDelete and OnAfterDelete events for taking action before/after the record is deleted. The TDataSource has an OnDataChange event to detect data changes at field level (aka which allows you to act on) - one note: this last event is also triggered, when the user moves to another record, so when data changes, you should check the state of the connected TDataset. – R. Hoek Apr 18 '21 at 14:27
  • @R.Hoek I am referring to the removal of the field value, not the complete records. I already tried the OnBefore/AfterDelete but no success—it is not even touched during runtime. I am not sure if ClientDataSet.Delete is doing its job on a per-record/row basis. But as to TDataSource event, I will have to look into it now. There are 3 events OnDataChange, OnSateChange, and OnUpdateData. I will get back to you on this. – RickyBelmont Apr 18 '21 at 14:41

1 Answers1

2

The code below should detect when a given field - Customer in this example - of your CDS has been modified so that either its AsString property is an empty string or the Field's value has been set to Null, but, in either case, only if the Field had a non-Null, non-empty value previously.

It uses a custom descendant of a TFieldDataLink to do the detection and handle the dsFieldChange event. This will only fire immediately while the Post method is executing and has not yet returned.

type
  TMyFieldDataLink = class(TFieldDataLink)
    procedure DataEvent(Event: TDataEvent; Info: Integer); override;
  end;

type
  TForm1 = class(TForm)
    ClientDataSet1: TClientDataSet;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSetProvider1: TDataSetProvider;
    DBNavigator1: TDBNavigator;
    DBEdit1: TDBEdit;
    procedure FormCreate(Sender: TObject);
  private
    FieldDataLink : TFieldDataLink;
  end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
  FieldDataLink := TMyFieldDataLink.Create;
  FieldDataLink.DataSource := DataSource1;
  FieldDataLink.FieldName := 'Company';
end;

{ TMyFieldDataLink }

procedure TMyFieldDataLink.DataEvent(Event: TDataEvent; Info: Integer);
var
  S : String;
  V : Variant;
begin
  inherited;

  if Event = deFieldChange then begin
    V :=  Field.OldValue;
    if not VarIsNull(V) then begin
      S := V;
      if (S <> '') and (Field <> Nil) and (Field.IsNull or (Field.AsString = '')) then begin
        ShowMessage('Field: ' + Field.FieldName + ' emptied');
      end;
    end;
  end;
end;

To use, cut & paste into a new VCL project.

Btw, for a problem like this, it's best to start by looking at it from the point of view of the dataset rather than a particular component like a DBGrid.

MartynA
  • 28,815
  • 3
  • 27
  • 68
  • As always, you astonished me with your answers! I have little issues here. When I run it fails, this error message pops `[dcc32 Error] main_u.pas(13): E2037 Declaration of 'DataEvent' differs from previous declaration`. I am not sure if this has something to do with the version. I am using Community Edition or did I missed something? – RickyBelmont Apr 19 '21 at 00:29
  • No worries at all! Please ignore my little issues. I found it from the TFieldDataLink declaration. I changed the Info: Integer to NativeInt -> `procedure DataEvent(Event: TDataEvent; Info: NativeInt); override;` and now it works! – RickyBelmont Apr 19 '21 at 00:39
  • Thank you so much! Greatly appreciate it! – RickyBelmont Apr 19 '21 at 00:40