TcxGrid 如何获取修改后的值并更新下一列的值?
TcxGrid how to get modified value and update the next column's value?
我正在使用 XE2。我想当用户在网格中编辑单价时,自动重新计算下一列的总价。
我应该使用哪个事件?
我尝试了TCXGridTableView OnEditValueChanged,但我无法获取修改后的值? AItem.EditValue 是修改前的值。
然后我尝试了 TcxGridColumn OnEditValueChanged。我可以这样得到修改后的值:
cxCE := Sender as TcxCurrencyEdit;
cxCE.Value; // this is the modified value
但是,如果用户修改了一个值,然后没有按 Enter,而是按 TAB 键离开,我遇到了一个问题,出现了一个有趣的问题:
- TcxGridColumn OnEditValueChanged 事件执行。
- 我仍然可以获得 cxCE.Value(修改后的值)并成功更新下一列的值。
- 修改下一列的值后,cxCE.Value变回修改前的值!
- 因此,用户输入被回滚,但下一列确实更新了。
发生了什么事的例子:
数量 |单价 |总价
2...... 5............ 10
当用户将单价从 5 修改为 7,然后按 Tab
在 OnEditValueChanged 之后,单价回滚但我的逻辑更新了总价:
2......5(回滚)14(更新)
如果有人能帮助我,我将不胜感激,非常感谢。
Which event I should use?
None 以上。我认为你正在以错误的方式解决这个问题。你似乎想要的是
当 Qty 或 UnitPrice 字段更改时自动更新 TotalPrice 字段。
最有效的思考方式是将其视为数据操作操作,
而不是 GUI 操作,这就是您应该对其进行编码的方式。
cxGrid 是一个 db-aware 组件,这些都是自动编码的
反映数据的变化,所以去更新 TotalPrice 的方法
字段是在对数据集进行操作的代码中执行,NOT 在代码中
它在 cxGrid 上运行。如果您尝试在 cxGrid 的代码中执行此操作,
你会发现自己不断 "fighting" 与网格在一起,因为它知道
如何成为 db-aware 而你实际上正在尝试颠覆它。
尝试下面的示例项目。建立一个新的VCL工程,添加一个TClientDataSet,
TDataSource 和 TDBNavigator 以及 "wire them up" 以通常的方式。
为 CDS 和 FormCreate 设置 OnCalcFields 事件处理程序
表单的事件,然后添加代码
如下所示。
项目运行时,动态创建一个cxGRid来显示数据(我做到了
这样是因为 cxGrid 中有太多的设置和子组件
在代码中创建一个最简单,而不是像这样在答案中指定其设置。
尝试更改 Qty 和 UnitPrice 字段中的值,然后
注意 TotalPrice 自动更新而不需要任何代码
它在 cxGrid 上运行。
type
TForm1 = class(TForm)
CDS1: TClientDataSet;
DS1: TDataSource;
DBNavigator1: TDBNavigator;
procedure FormCreate(Sender: TObject);
procedure CDS1CalcFields(DataSet: TDataSet);
private
public
cxGrid : TcxGrid;
cxLevel : TcxGridLevel;
cxView : TcxGridDBTableView;
end;
[...]
// This is a utility function to create TFields in code
function CreateField(AFieldClass : TFieldClass; AOwner : TComponent; ADataSet : TDataSet;
AFieldName, AName : String; ASize : Integer; AFieldKind : TFieldKind) : TField;
begin
Result := AFieldClass.Create(AOwner);
Result.FieldKind := AFieldKind;
Result.FieldName := AFieldName;
Result.Name := AName;
Result.Size := ASize;
Result.DataSet := ADataSet;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i : Integer;
Field : TField;
Col : TcxGridDBColumn;
begin
// First, create the Fields of the ClientDataSet
Field := CreateField(TIntegerField, Self, CDS1, 'ID', 'CDS1ID', 0, fkData);
Field := CreateField(TIntegerField, Self, CDS1, 'Qty', 'CDS1Qty', 0, fkData);
Field := CreateField(TCurrencyField, Self, CDS1, 'UnitPrice', 'CDS1UnitPrice', 0, fkData);
Field := CreateField(TCurrencyField, Self, CDS1, 'TotalPrice', 'CDS1TotalPrice', 0, fkInternalCalc);
// Field.ReadOnly := True;
CDS1.CreateDataSet;
CDS1.IndexFieldNames := 'ID';
// Next, populate the CDS with a few records
// Note : If we are using calculated fields, we do to need to specify
// a value for the TotalPriced field
CDS1.InsertRecord([1, 1, 1]);
CDS1.InsertRecord([2, 2, 5]);
CDS1.InsertRecord([3, 3, 6]);
CDS1.First;
// Now, create a cxGrid to display the CDS data
cxGrid := TcxGrid.Create(Self);
cxGrid.Parent := Self;
cxGrid.Width := 400;
cxLevel := cxGrid.Levels.Add;
cxLevel.Name := 'Firstlevel';
cxView := cxGrid.CreateView(TcxGridDBTableView) as TcxGridDBTableView;
cxView.Name := 'ATableView';
cxView.DataController.KeyFieldNames := 'ID';
cxView.DataController.Options := cxView.DataController.Options + [dcoImmediatePost];
cxLevel.GridView := cxView;
cxView.DataController.DataSource := DS1;
cxView.DataController.CreateAllItems;
// Since the TotalPrice column is a calculated field, we need to
// prevent the user from attempting to edit it
Col := cxView.GetColumnByFieldName('TotalPrice');
Col.Options.Editing := False;
ActiveControl := cxGrid;
end;
// Procedure to calculate the TotalPrice field
procedure CalculateTotalPrice(DataSet : TDataSet);
var
Qty : Integer;
UnitPrice,
TotalPrice : Currency;
begin
Qty := DataSet.FieldByName('Qty').AsInteger;
UnitPrice := DataSet.FieldByName('UnitPrice').AsCurrency;
TotalPrice := Qty * UnitPrice;
DataSet.FieldByName('TotalPrice').AsCurrency := TotalPrice;
end;
procedure TForm1.CDS1CalcFields(DataSet: TDataSet);
begin
CalculateTotalPrice(DataSet);
end;
我正在使用 XE2。我想当用户在网格中编辑单价时,自动重新计算下一列的总价。
我应该使用哪个事件?
我尝试了TCXGridTableView OnEditValueChanged,但我无法获取修改后的值? AItem.EditValue 是修改前的值。
然后我尝试了 TcxGridColumn OnEditValueChanged。我可以这样得到修改后的值:
cxCE := Sender as TcxCurrencyEdit;
cxCE.Value; // this is the modified value
但是,如果用户修改了一个值,然后没有按 Enter,而是按 TAB 键离开,我遇到了一个问题,出现了一个有趣的问题:
- TcxGridColumn OnEditValueChanged 事件执行。
- 我仍然可以获得 cxCE.Value(修改后的值)并成功更新下一列的值。
- 修改下一列的值后,cxCE.Value变回修改前的值!
- 因此,用户输入被回滚,但下一列确实更新了。
发生了什么事的例子:
数量 |单价 |总价
2...... 5............ 10
当用户将单价从 5 修改为 7,然后按 Tab 在 OnEditValueChanged 之后,单价回滚但我的逻辑更新了总价:
2......5(回滚)14(更新)
如果有人能帮助我,我将不胜感激,非常感谢。
Which event I should use?
None 以上。我认为你正在以错误的方式解决这个问题。你似乎想要的是 当 Qty 或 UnitPrice 字段更改时自动更新 TotalPrice 字段。 最有效的思考方式是将其视为数据操作操作, 而不是 GUI 操作,这就是您应该对其进行编码的方式。
cxGrid 是一个 db-aware 组件,这些都是自动编码的 反映数据的变化,所以去更新 TotalPrice 的方法 字段是在对数据集进行操作的代码中执行,NOT 在代码中 它在 cxGrid 上运行。如果您尝试在 cxGrid 的代码中执行此操作, 你会发现自己不断 "fighting" 与网格在一起,因为它知道 如何成为 db-aware 而你实际上正在尝试颠覆它。
尝试下面的示例项目。建立一个新的VCL工程,添加一个TClientDataSet, TDataSource 和 TDBNavigator 以及 "wire them up" 以通常的方式。
为 CDS 和 FormCreate 设置 OnCalcFields 事件处理程序 表单的事件,然后添加代码 如下所示。
项目运行时,动态创建一个cxGRid来显示数据(我做到了 这样是因为 cxGrid 中有太多的设置和子组件 在代码中创建一个最简单,而不是像这样在答案中指定其设置。
尝试更改 Qty 和 UnitPrice 字段中的值,然后 注意 TotalPrice 自动更新而不需要任何代码 它在 cxGrid 上运行。
type
TForm1 = class(TForm)
CDS1: TClientDataSet;
DS1: TDataSource;
DBNavigator1: TDBNavigator;
procedure FormCreate(Sender: TObject);
procedure CDS1CalcFields(DataSet: TDataSet);
private
public
cxGrid : TcxGrid;
cxLevel : TcxGridLevel;
cxView : TcxGridDBTableView;
end;
[...]
// This is a utility function to create TFields in code
function CreateField(AFieldClass : TFieldClass; AOwner : TComponent; ADataSet : TDataSet;
AFieldName, AName : String; ASize : Integer; AFieldKind : TFieldKind) : TField;
begin
Result := AFieldClass.Create(AOwner);
Result.FieldKind := AFieldKind;
Result.FieldName := AFieldName;
Result.Name := AName;
Result.Size := ASize;
Result.DataSet := ADataSet;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i : Integer;
Field : TField;
Col : TcxGridDBColumn;
begin
// First, create the Fields of the ClientDataSet
Field := CreateField(TIntegerField, Self, CDS1, 'ID', 'CDS1ID', 0, fkData);
Field := CreateField(TIntegerField, Self, CDS1, 'Qty', 'CDS1Qty', 0, fkData);
Field := CreateField(TCurrencyField, Self, CDS1, 'UnitPrice', 'CDS1UnitPrice', 0, fkData);
Field := CreateField(TCurrencyField, Self, CDS1, 'TotalPrice', 'CDS1TotalPrice', 0, fkInternalCalc);
// Field.ReadOnly := True;
CDS1.CreateDataSet;
CDS1.IndexFieldNames := 'ID';
// Next, populate the CDS with a few records
// Note : If we are using calculated fields, we do to need to specify
// a value for the TotalPriced field
CDS1.InsertRecord([1, 1, 1]);
CDS1.InsertRecord([2, 2, 5]);
CDS1.InsertRecord([3, 3, 6]);
CDS1.First;
// Now, create a cxGrid to display the CDS data
cxGrid := TcxGrid.Create(Self);
cxGrid.Parent := Self;
cxGrid.Width := 400;
cxLevel := cxGrid.Levels.Add;
cxLevel.Name := 'Firstlevel';
cxView := cxGrid.CreateView(TcxGridDBTableView) as TcxGridDBTableView;
cxView.Name := 'ATableView';
cxView.DataController.KeyFieldNames := 'ID';
cxView.DataController.Options := cxView.DataController.Options + [dcoImmediatePost];
cxLevel.GridView := cxView;
cxView.DataController.DataSource := DS1;
cxView.DataController.CreateAllItems;
// Since the TotalPrice column is a calculated field, we need to
// prevent the user from attempting to edit it
Col := cxView.GetColumnByFieldName('TotalPrice');
Col.Options.Editing := False;
ActiveControl := cxGrid;
end;
// Procedure to calculate the TotalPrice field
procedure CalculateTotalPrice(DataSet : TDataSet);
var
Qty : Integer;
UnitPrice,
TotalPrice : Currency;
begin
Qty := DataSet.FieldByName('Qty').AsInteger;
UnitPrice := DataSet.FieldByName('UnitPrice').AsCurrency;
TotalPrice := Qty * UnitPrice;
DataSet.FieldByName('TotalPrice').AsCurrency := TotalPrice;
end;
procedure TForm1.CDS1CalcFields(DataSet: TDataSet);
begin
CalculateTotalPrice(DataSet);
end;