It is simple to get the cxGrid to draw a cell (or row) highlighted in some way using the
cxGrid1DBTableView1CustomDrawCell
event. And by having a flag that indicates that the OnCloseQuery event is in progress, you can restrict its action to inside that event.
Update The code I originally posted with this answer could not successfully mark more than one cell in the current grid row as changed. The updated code below can do this however; note the comments in the two
procedures.
type
TForm1 = class(TForm)
[...]
public
QueryingClose : Boolean;
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
try
QueryingClose := True;
//{cxGrid1.Invalidate{True); Do NOT call Invalidate, because it causes the
// grid's repainting logic to operate in a way which effectively makes it
// impossible to mark more that one cell in the current data row as changed
ShowMessage('Close?');
finally
QueryingClose := False;
end;
end;
procedure TForm1.cxGrid1DBTableView1CustomDrawCell(Sender:
TcxCustomGridTableView; ACanvas: TcxCanvas; AViewInfo:
TcxGridTableDataCellViewInfo; var ADone: Boolean);
var
Field : TField;
MarkCell : Boolean;
S1,
S2 : String;
EC : TcxGridTableEditingController;
begin
if QueryingClose and
(TcxGridDBTableView(Sender).DataController.DataSet.State in[dsEdit, dsInsert]) then begin
Field := TcxGridDBColumn(AViewInfo.Item).DataBinding.Field;
S1 := VarToStr(Field.OldValue);
// When this event is called, the user may be in the middle of editing a cell's contents
// So, the purpose of the following lines is to close the inplace editor being used to do
// this amd post the chamged value back to the TField associated with the cell
EC := TcxGridDBTableView(Sender).Controller.EditingController;
if EC.IsEditing then
EC.HideEdit(True);
S2 := VarToStr(Field.Value);
MarkCell := S1 <> S2;
if MarkCell then
ACanvas.Brush.Color := clLime;
end;
end;
For this to work, your TDataSet-descendant type must support correctly returning the original contents of the fields on their OldValue property; TClientDataSet, which I've used to write/test this code certainly does this but I've no idea what actual TDataSet type you're using.
Hopefully, it should be apparent that you could use these two procedures to
build a list of TFields that have changed values, including the FieldName OldValue, and Value.