Delphi - 如何确定 TDataSet 在插入/编辑状态时是否已被修改?

8

当数据集处于插入状态时,我该如何找出数据感知组件字段是否已被修改?我想知道一个字段是否“真正”被修改。(我不在意用户是否在字段中输入了一些内容,然后将其全部删除,这意味着发生了修改)。

DataSet.ModifiedDataSet.UpdateStatusChangeCount都无法解决我的问题。

LE: 让我更深入地解释一下。因此,初始数据集如下:

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

插入后

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

当数据集被真正修改时
-------------------------------------
|PK  | Field1| Field2| Field3|Field4|
-------------------------------------
| 2  | avalue|       |       |      |  
-------------------------------------
| 1  |  a    | b     | c     | d    |  
-------------------------------------

直接询问控件怎么样?我的意思是例如TDBEdit.Modified?请将此作为一个简单的提醒,我不是一个数据库控件的使用者:-) - TLama
1
@TLama - 问题在于,这是包含在一个被广泛使用的主表单/框架中。因此,我正在尝试找到一种通用解决方案,可以在数据集已经处于插入/编辑模式时指示它是否“真正”更改了... - RBA
2
这个问题没有数据集级别的解决方法,只有数据感知控件的解决方法。 - Warren P
6个回答

8
您可以在AfterInsert/AfterEdit事件中修改DataSetModified属性(并设置初始/默认值),然后在提交之前测试DataSet.Modified
为确定哪些字段已被修改,我保存了初始记录的副本。
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;

好的,这是一个抽象的想法。你可以像我一样对TDataSet进行子类化,并在TDataSet组件内直接实现此功能。


3
当状态为dsInsert时,请使用以下代码:
VarCompareValue(Field.NewValue, Unassigned) = vrNotEqual;

dsEdit使用说明:

OldValue <> Value;

在dsInsert状态下不要使用此功能,因为在数字字段中0等同于未分配:

Field.NewValue <> Unassigned

1
这意味着要验证字段不为空...变量为空。 - RBA
没太明白你的评论,我发布的代码必须在实际记录中进行验证,通常在BeforePost事件中,它会告诉你是否“真正”修改了。在Post事件之后,只需检查ChangeCount即可。 - Fabio Gomes

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;

抱歉,我需要将标签设置为0,并且当然要在OnBeforeEdit中进行 :) - Branko Radošević
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

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;

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

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;

0

到目前为止,我找到了一个似乎有效的解决方案。解决方案包括: - 将数据源链接到TDataSet的派生类 - 在数据集OnAfterScroll事件中将全局布尔变量设置为false,在数据源的OnDataChange事件中将其设置为true。

从我对应用程序进行的测试来看,这个解决方法似乎是有效的。现在我需要调查所有发生的事件,并需要特殊处理以不改变全局变量的状态。

欢迎提出任何其他想法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接