Delphi - TDataSet determine if it was modified when is in insert/edit state
Asked Answered
C

6

8

how can I find out if an data-aware component field has been modified when the dataset is already in Insert state? I want to know if a field was 'really' modified. (I don't care if the user has input something in a field and after that erase everything, this mean that a modification occured).

DataSet.Modified, DataSet.UpdateStatus or ChangeCount are not solving my problem.

LE: let me explain more in depth this. so, initial dataset looks like

-------------------------------------
|PK  | Field1| Field2| Field3|Field4|
-------------------------------------
| 1  |  a    | b     | c     | d    |  
-------------------------------------

after insert

-------------------------------------
|PK  | Field1| Field2| Field3|Field4|
-------------------------------------
| 2  |       |       |       |      |  
-------------------------------------
| 1  |  a    | b     | c     | d    |  
-------------------------------------

when the dataset is really modified

-------------------------------------
|PK  | Field1| Field2| Field3|Field4|
-------------------------------------
| 2  | avalue|       |       |      |  
-------------------------------------
| 1  |  a    | b     | c     | d    |  
-------------------------------------
Crysta answered 4/4, 2012 at 14:49 Comment(3)
What about to ask directly the control ? I mean e.g. TDBEdit.Modified ? Take it as a lame note, please, I'm not a DB aware controls user :-)Fda
@Fda - the problem is that, this is contained by a master form/frame heavily used. So, I'm trying to find a generic solution, which can indicate me if the dataset 'really' changed when is already in an insert/edit mode...Crysta
There is no dataset level way to do this. Only a data aware control way to do this.Citarella
U
8

You could hack the DataSet to change it's Modified property on AfterInsert/AfterEdit (and set initial/default values) and later test for DataSet.Modified (e.g. on before post).
To determine which specific fields were modified, I hold a copy of the initial record e.g.:

type
  TDataRecord = array of record
    FieldName: string;
    Value: Variant;
  end;

type
  TForm1 = class(TForm)
    ... 
  private
    FInitRecord, FPostRecord: TDataRecord;
  end;

function GetDataRecord(DataSet: TDataSet): TDataRecord;
var
  I: Integer;
begin
  Result := nil;
  if Assigned(DataSet) then begin
    SetLength(Result, DataSet.FieldCount);
    for I := 0 to DataSet.FieldCount - 1 do begin
      Result[I].FieldName := DataSet.Fields[I].FieldName;
      Result[I].Value := DataSet.Fields[I].Value;
    end;
  end;
end;

type
  TDataSetAccess = class(TDataSet);

procedure TForm1.ADODataSet1AfterInsert(DataSet: TDataSet);
begin
  // set initial values 
  ADODataSet1.FieldByName('PK').Value := GetMyPKValue;
  ADODataSet1.FieldByName('DateCreated').AsDateTime := Now(); 
  // un-modify
  TDataSetAccess(ADODataSet1).SetModified(False);
  // save initial record
  FInitRecord := GetDataRecord(ADODataSet1);
end;    

procedure TForm1.ADODataSet1BeforePost(DataSet: TDataSet);
var
  I: Integer;
begin
  if ADODataSet1.Modified then
  begin
    FPostRecord := GetDataRecord(ADODataSet1);
    Memo1.Lines.Clear;
    for I := 0 to Length(FPostRecord) - 1 do begin
      if FPostRecord[I].Value <> FInitRecord[I].Value then
        Memo1.Lines.Add(Format('Field %s was modified', [FPostRecord[I].FieldName]));
    end;
  end;
end;

Well, It's the abstract idea anyway. You could sub-class your TDataSet like I do, and implement this feature directly inside your TDataSet component.

Urbannai answered 5/4, 2012 at 7:47 Comment(0)
I
3

When the state is dsInsert use:

VarCompareValue(Field.NewValue, Unassigned) = vrNotEqual;

dsEdit use:

OldValue <> Value;

Do not use this in dsInsert state as in numeric Fields 0 is equal Unassigned:

Field.NewValue <> Unassigned
Ikhnaton answered 4/4, 2012 at 16:2 Comment(2)
this means to verify is the field is not null...variant nullCrysta
Didn't quite get your comment, the code I posted have to be verified in the actual record, usually in the BeforePost event, it will tell you if it was "really" modified. After the Post event just check for the ChangeCount.Ikhnaton
C
0

Until now I found a solution, which seems to work. Solution consist in: - link a datasource to the tdataset descendant - a global boolean variable is set to false on the dataset OnAfterScroll event, and true on the datasources's OnDataChange event.

From the tests I performed on the application until now, it seems that this work-around is working. I need now to investigate all the events which occur and need special treatment for not altering the state of the global variable in the process.

Any other ideas are welcomed

Crysta answered 5/4, 2012 at 14:12 Comment(0)
R
0
//The tag from the dataset will be 1 if the dataset has become dirty :)

type
  Tsomeform = class(TForm)
  //...
  MyDataset: TClientDataSet;
private
  afs: array of TFieldSetTextEvent;
public
  procedure SetIsDirty(Sender: TField; const Text: string);
end;

procedure Tsomeform.FormCreate(Sender: TObject);
begin
  SetLength(afs,MyDataset.FieldCount);
end;

procedure Tsomeform.FormShow(Sender: TObject); 
var i: integer;
begin
  for i := 0 to MyDataset.FieldCount-1 do
  begin
    afs[i] := MyDataset.Fields[i].OnSetText;
    MyDataset.Fields[i].OnSetText := SetIsDirty;
    MyDataset.Fields[i].Tag := i;
  end;
end;

procedure Tsomeform.MyDatasetBeforeInsert(DataSet: TDataSet);
    begin
      MyDataset.Tag := 0;
    end;
    
    procedure Tsomeform.MyDatasetBeforeEdit(DataSet: TDataSet);
    begin
      MyDataset.Tag := 0;
    end;
    
    procedure Tsomeform.SetIsDirty(Sender: TField; const Text: string);
    begin
      //Execute any potential TFieldSetTextEvent in any field of MyDataset
      if Assigned(afs[Sender.Tag]) then
        afs[Sender.Tag](Sender,Text);
      if VarToStr(Sender.Value)<>Text then 
        MyDataset.Tag := 1;
      Sender.Value := Text;//It is important to enabling the change.
    end;
Revitalize answered 6/5, 2022 at 14:28 Comment(2)
Sorry I would have to set the tag to 0 and to OnBeforeEdit of course :)Bilharziasis
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Electret
R
0
procedure Tsomeform.MyDatasetBeforeInsert(DataSet: TDataSet);
begin
  MyDataset.Tag := 0;
end;

procedure Tsomeform.MyDatasetBeforeEdit(DataSet: TDataSet);
begin
  MyDataset.Tag := 0;
end;

procedure Tsomeform.SetIsDirty(Sender: TField; const Text: string);
begin
  if VarToStr(Sender.Value)<>Text then 
    MyDataset.Tag := 1;
end;

procedure Tsomeform.FormShow(Sender: TObject); 
var i: integer;
begin
  for i := 0 to MyDataset.FieldCount-1 do
  begin
    MyDataset.Fields[i].OnSetText := SetIsDirty;
  end;
end;
Revitalize answered 6/5, 2022 at 14:49 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Electret
R
0
//The tag from the dataset will be 1 if the dataset has become dirty :)

type
  Tsomeform = class(TForm)
  //...
private
afs: array of TFieldSetTextEvent;
public

end;

procedure Tsomeform.FormCreate(Sender: TObject);
begin
  SetLength(afs,MyDataset.FieldCount);
end;

procedure Tsomeform.FormShow(Sender: TObject); 
var i: integer;
begin
  for i := 0 to MyDataset.FieldCount-1 do
  begin
    afs[i] := MyDataset.Fields[i].OnSetText;
    MyDataset.Fields[i].OnSetText := SetIsDirty;
    MyDataset.Fields[i].Tag := i;
  end;
end;

procedure Tsomeform.MyDatasetBeforeInsert(DataSet: TDataSet);
    begin
      MyDataset.Tag := 0;
    end;
    
    procedure Tsomeform.MyDatasetBeforeEdit(DataSet: TDataSet);
    begin
      MyDataset.Tag := 0;
    end;
    
    procedure Tsomeform.SetIsDirty(Sender: TField; const Text: string);
    begin
      //Execute any potential TFieldSetTextEvent in any field of MyDataset
      if Assigned(afs[Sender.Tag]) then
        afs[Sender.Tag](Sender,Text);
      if VarToStr(Sender.Value)<>Text then 
        MyDataset.Tag := 1;
      Sender.Value := Text;//It is important to enable change
    end;
Revitalize answered 12/5, 2022 at 8:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.