I am migrating application from Delphi 6 / TdxDBGrid's to the Delphi 2009 / TcxGrid's and I have the following problem:
I had TdxDBGrid with grid level OnEdited event that assigned "IS_MODIFIED_BY_USER" attribute to the edited record:
procedure TSalesOrderForm.LineGridEdited(Sender: TObject;
Node: TdxTreeListNode);
begin
inherited;
if LineGrid.FocusedField<>LineGridIS_MODIFIED_BY_USER.Field then
(Sender as TdxDBGrid).DataSource.
DataSet.FieldByName('IS_MODIFIED_BY_USER').AsInteger:=1;
end;
Such implementation was very smooth. It handled updates coming from grid (user) only and IS_MODIFIED_BY_USER stayed intact if the record was modified by the DataModule (i.e. by the processes that were not direct editing of the user). It worked for edits of any field in the grid.
Now I am trying to move this functionality to the TcxGrid/TcxDBTableView and I am looking on grid level OnEditValueChanged and on the Column-Editor-Properties.OnEditValueChanged.
Such event is fired indeed, but any attempt to change another field (e.g. assign 1 to the IS_MODIFIED_BY_USER when the AMOUNT field is changed) fails spectacularly: the value is assigned (to the IS_MODIFIED_BY_USER), but the edited value (of AMOUNT) is ignored by the grid and upon pressing the Enter (while keeping cursor in the AMOUNT field) the value of AMOUNT reverts to the initial values.
I have tried different assignment strategiess - via DataSet, via Grid Editors, but nothing works.
Other alternatives are not goods as well. E.g. TDataSource.OnUpdateDate is not solution, because it works on the Post only, but I need to assign 1 to the IS_MODIFIED_BY_USER immediately after completion of edit and there may be many other edits in the same record before the whole record is posted.
Another approach would be the use of TClientDataSetAMOUNT.OnChange event handler, but it is not the solution, because this event is fired upon any changes of the dataset record. But I need to catch only changes coming from the user edits in the grid. Another improvement would be the use of flat, that prevents update and set this flag for any DataModule initiated changes:
procedure TMainForm.CDSAMOUNTChange(Sender: TField); begin if (FSystemUpdates) then Exit; if CDSIS_MODIFIED_BY_USER.AsInteger<>1 then CDSIS_MODIFIED_BY_USER.AsInteger:=1; end;
This can work indeed, but this is very involved approach.
I still hope that I can tame the TcxDBTableView to respond to the user-made edits.
Here is my test project that demonstrates the problem:
pas:
unit MainFormU;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, DBClient, StdCtrls, cxStyles, dxSkinsCore, dxSkinBlack,
dxSkinBlue, dxSkinCaramel, dxSkinCoffee, dxSkinDarkSide, dxSkinGlassOceans,
dxSkiniMaginary, dxSkinLilian, dxSkinLiquidSky, dxSkinLondonLiquidSky,
dxSkinMcSkin, dxSkinMoneyTwins, dxSkinOffice2007Black, dxSkinOffice2007Blue,
dxSkinOffice2007Green, dxSkinOffice2007Pink, dxSkinOffice2007Silver,
dxSkinPumpkin, dxSkinSilver, dxSkinSpringTime, dxSkinStardust,
dxSkinSummer2008, dxSkinsDefaultPainters, dxSkinValentine, dxSkinXmas2008Blue,
dxSkinscxPCPainter, cxCustomData, cxGraphics, cxFilter, cxData, cxDataStorage,
cxEdit, cxDBData, cxGridCustomTableView, cxGridTableView, cxGridDBTableView,
cxGridLevel, cxClasses, cxControls, cxGridCustomView, cxGrid, cxMaskEdit;
type
TMainForm = class(TForm)
SetupBtn: TButton;
CDS: TClientDataSet;
DS: TDataSource;
CDSID: TIntegerField;
CDSAMOUNT: TFloatField;
CDSAMOUNT_2: TFloatField;
DataGridDBTableView: TcxGridDBTableView;
DataGridLevel: TcxGridLevel;
DataGrid: TcxGrid;
DataGridDBTableViewID: TcxGridDBColumn;
DataGridDBTableViewAMOUNT: TcxGridDBColumn;
DataGridDBTableViewAMOUNT_2: TcxGridDBColumn;
procedure SetupBtnClick(Sender: TObject);
procedure DataGridDBTableViewAMOUNTPropertiesEditValueChanged(
Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.DataGridDBTableViewAMOUNTPropertiesEditValueChanged(
Sender: TObject);
begin
CDSAMOUNT_2.AsFloat:=500;
//DataGridDBTableViewAMOUNT_2.EditValue:=2.0*DataGridDBTableViewAMOUNT.EditValue;
end;
procedure TMainForm.SetupBtnClick(Sender: TObject);
var i: Integer;
begin
if CDS.Active then
CDS.EmptyDataSet
else
CDS.CreateDataSet;
DataGridDBTableView.BeginUpdate;
try
for i := 0 to 100 do begin
CDS.Append;
CDSID.AsInteger:=i;
CDSAMOUNT.AsFloat:=i*100.11;
CDSAMOUNT_2.AsFloat:=i*1000.11;
CDS.Post;
end;
finally
DataGridDBTableView.EndUpdate;
end;
end;
end.
And dfm:
object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'MainForm'
ClientHeight = 400
ClientWidth = 784
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object SetupBtn: TButton
Left = 288
Top = 351
Width = 75
Height = 25
Caption = 'SetupBtn'
TabOrder = 0
OnClick = SetupBtnClick
end
object DataGrid: TcxGrid
Left = 8
Top = 8
Width = 761
Height = 266
TabOrder = 1
object DataGridDBTableView: TcxGridDBTableView
NavigatorButtons.ConfirmDelete = False
DataController.DataSource = DS
DataController.Summary.DefaultGroupSummaryItems = <>
DataController.Summary.FooterSummaryItems = <>
DataController.Summary.SummaryGroups = <>
object DataGridDBTableViewID: TcxGridDBColumn
DataBinding.FieldName = 'ID'
PropertiesClassName = 'TcxMaskEditProperties'
Width = 154
end
object DataGridDBTableViewAMOUNT: TcxGridDBColumn
DataBinding.FieldName = 'AMOUNT'
PropertiesClassName = 'TcxMaskEditProperties'
Properties.OnEditValueChanged = DataGridDBTableViewAMOUNTPropertiesEditValueChanged
Width = 220
end
object DataGridDBTableViewAMOUNT_2: TcxGridDBColumn
DataBinding.FieldName = 'AMOUNT_2'
PropertiesClassName = 'TcxMaskEditProperties'
Width = 156
end
end
object DataGridLevel: TcxGridLevel
GridView = DataGridDBTableView
end
end
object CDS: TClientDataSet
Aggregates = <>
Params = <>
Left = 512
Top = 336
object CDSID: TIntegerField
FieldName = 'ID'
end
object CDSAMOUNT: TFloatField
FieldName = 'AMOUNT'
end
object CDSAMOUNT_2: TFloatField
FieldName = 'AMOUNT_2'
end
end
object DS: TDataSource
DataSet = CDS
Left = 424
Top = 344
end
end
I narrowed down my problem to the requirement "Change another TcxDBTableView column value while changing one TcxDBTableView column" and it had solution from DevExpress https://supportcenter.devexpress.com/ticket/details/a343/how-to-set-a-value-of-another-grid-column-during-editing-of-a-cell
In the particular case of the code provided by OP there are 2 variants:
If EditValueChanged is handled for the column, then the code is:
But if the EditValueChanged is handled for the DBTableView, then the code is:
cxDataUtils should be added to the uses.
There are 2 crucial points: 1) the value of the current column should be posted by finding editor and calling
PostEditValue
; 2) Direct assignment to DataSet orDataController.Values[...]
does not work, one shoule set value viaSetEditValue(...)
.