TClientDataSet 中计算字段有两种: Calculated(计算字段)、InternalCalc(内部计算字段).
两者区别是: Calculated 在每次需要时都要重新计算; InternalCalc 只需要计算一次.
Calculated 需要计算的时间, InternalCalc 需要存取的时间; 当然后者快.
计算字段不会被保存到文件.
下面的例子先定义了两个整数字段: sum1、sum2;
又定义了两个计算字段: SUM(求和)、MUL(求积), 分别指定了 Calculated、InternalCalc.
计算是在 TClientDataSet 的 OnCalcFields 事件中完成的, 每当需要计算结果时事件会被自动激活.
除了 OnCalcFields 事件中的代码外, 其他都可以在设计时完成; 这里是动态完成的.
//准备: 窗体放置 ClientDataSet1、DataSource1、DBGrid1(关联一下)、Button1 //程序运行后, 可用 Tab 和 ↑ ↓ → ← 键配合着输入测试数据. { 建立数据集, 包括计算字段 } procedure TForm1.Button1Click(Sender: TObject); begin with TIntegerField.Create(Self) do begin FieldName := 'num1'; { FieldKind 的默认值是 fkData } DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'num2'; DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'SUM'; FieldKind := fkCalculated; { 指定为计算字段 } DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'MUL'; FieldKind := fkInternalCalc; { 指定为内部计算字段 } DataSet := ClientDataSet1; end; ClientDataSet1.CreateDataSet; Button1.Enabled := False; end; { OnCalcFields 事件 } procedure TForm1.ClientDataSet1CalcFields(DataSet: TDataSet); begin { 获取计算字段的值 } DataSet['SUM'] := DataSet['num1'] + DataSet['num2']; { 获取内部计算字段的值; 一般要先判断一下以避免重复运算 } if DataSet.State = dsInternalCalc then DataSet['MUL'] := DataSet['num1'] * DataSet['num2']; end;
在上面程序的基础上再添加一个 Timer1, 为详细测试 Calculated 和 InternalCalc 的区别, 代码修改如下:
var Calc, InternalCalc: Integer; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin with TIntegerField.Create(Self) do begin FieldName := 'num1'; DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'num2'; DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'SUM'; FieldKind := fkCalculated; DataSet := ClientDataSet1; end; with TIntegerField.Create(Self) do begin FieldName := 'MUL'; FieldKind := fkInternalCalc; { 指定为内部计算字段 } DataSet := ClientDataSet1; end; ClientDataSet1.CreateDataSet; { 添加测试数据 } ClientDataSet1.DisableControls; for i := 0 to 999 do ClientDataSet1.AppendRecord([i, i]); ClientDataSet1.EnableControls; Button1.Enabled := False; end; procedure TForm1.ClientDataSet1CalcFields(DataSet: TDataSet); begin Inc(Calc); DataSet['SUM'] := DataSet['num1'] + DataSet['num2']; if DataSet.State = dsInternalCalc then begin Inc(InternalCalc); DataSet['MUL'] := DataSet['num1'] * DataSet['num2']; end; end; procedure TForm1.Timer1Timer(Sender: TObject); begin Text := Format('Calc: %d; InternalCalc: %d', [Calc, InternalCalc]); end;
测试图:
TDataSetState.State: TDataSetState;
TDataSetState = ( dsInactive, { 数据集被关闭 } dsBrowse, { 浏览模式 } dsEdit, { 编辑模式, 意味着 Edit 方法已被调用, 而编辑后的数据尚未被提交 } dsInsert, { 插入模式, 即 insert 被调用, 但变化还没有提交 } dsSetKey, { 设置键值模式, 意味着 SetKey 被调用, 而 GotoKey 尚未被调用 } dsCalcFields, { OnCalcFields 事件已发生, 对记录值的计算正在进行中 } dsFilter, { 数据集正在处理一个记录过滤器、查找字段或其他需要用到过滤器的操作 } dsNewValue, { 数据集处于 NewValue 属性被访问的临时状态 } dsOldValue, { 数据集处于 OldValue 属性被访问的临时状态 } dsCurValue, { 数据集处于 CurValue 属性被访问的临时状态 } dsBlockRead, { 数据正被写入缓冲区, 此时数据库表中指针的移动并不触发数据感知组件的更新和事件的发生 } dsInternalCalc, { 一个字段值正在被计算, 以供一个有 fkInterternalCalc 类型的 Fieldkind 属性的字段使用 } dsOpening { 数据集处于正在打开状态但是还没有结束, 这种状态发生在数据集被异步打开时 } );