Delphi 10.4:提交数据库前如何实时显示TDBGrid复选框本地状态?
问题场景与需求
在Delphi 10.4环境下,使用Firebird数据库,表中包含布尔字段PickedUp;通过TFDQuery作为数据集,TDBGrid展示文档记录,TDBCheckBox关联该字段。
需求:点击TDBGrid内的复选框时,状态变更暂存到本地数组,点击Apply按钮后再批量提交到数据库。
当前问题:点击复选框后状态已在本地切换,但无法实时看到变更,只有提交并刷新数据集后才可见,需要实现提交前就能查看状态变化的效果。
现有代码
窗体OnCreate事件
dbcheckbox1.DataSource := dsOutBound; dbcheckbox1.DataField := 'PickedUp'; dbcheckbox1.Visible := False; dbcheckbox1.Color := dbgOutBound.Color; dbcheckbox1.Caption := ''; dbcheckbox1.Parent := dbgOutBound;
其他事件代码
type TDocumentInfo = record DocumentID: Integer; PickedUp: Boolean; end; var DocumentsToUpdate: TArray<TDocumentInfo>; procedure Tform1.dbgrid1CellClick(Column: TColumn); var DocumentID: Integer; PickedUp: Boolean; begin if Column.Field.DataType = ftBoolean then begin // Store the changes locally without immediately updating the database DocumentID := dbgrid1.DataSource.DataSet.FieldByName('DOCUMENTID').AsInteger; PickedUp := not Column.Field.AsBoolean; // Toggle the value SetLength(DocumentsToUpdate, Length(DocumentsToUpdate) + 1); DocumentsToUpdate[High(DocumentsToUpdate)].DocumentID := DocumentID; DocumentsToUpdate[High(DocumentsToUpdate)].PickedUp := PickedUp; // Update the checkbox state locally Column.Field.AsBoolean := PickedUp; end; end; procedure Tform1.UpdateCheckedDocumentsPickedUp; var i: Integer; Bookmark: TBookmark; DocumentID: Integer; PickedUp: Boolean; begin if Length(DocumentsToUpdate) = 0 then Exit; Bookmark := dbgrid1.DataSource.DataSet.GetBookmark; dbgrid1.DataSource.DataSet.DisableControls; try for i := Low(DocumentsToUpdate) to High(DocumentsToUpdate) do begin DocumentID := DocumentsToUpdate[i].DocumentID; PickedUp := DocumentsToUpdate[i].PickedUp; // Locate the record by DocumentID if dbgrid1.DataSource.DataSet.Locate('DOCUMENTID', DocumentID, []) then begin // Apply the change only for the checked records dbgrid1.DataSource.DataSet.Edit; dbgrid1.DataSource.DataSet.FieldByName('PICKEDUP').AsBoolean := PickedUp; dbgrid1.DataSource.DataSet.Post; end; end; finally dbgrid1.DataSource.DataSet.GotoBookmark(Bookmark); dbgrid1.DataSource.DataSet.FreeBookmark(Bookmark); dbgrid1.DataSource.DataSet.EnableControls; end; // Clear the local changes after applying them SetLength(DocumentsToUpdate, 0); end; procedure Tform1.Button1Click(Sender: TObject); begin UpdateCheckedDocumentsPickedUp; end; procedure Tform1.dbgrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); const CtrlState: array[Boolean] of integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED) ; IsChecked : array[Boolean] of Integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED); begin if Column.FieldName = 'PICKEDUP' then if Column.Field.AsBoolean then DrawGridCheckBox(dbgrid1.Canvas, Rect, true) else DrawGridCheckBox(dbgrid1.Canvas, Rect, false); end; procedure Tform1.DrawGridCheckBox(Canvas: TCanvas; Rect: TRect; Checked: boolean); var DrawFlags: Integer; begin Canvas.TextRect(Rect, Rect.Left + 1, Rect.Top + 1, ' '); DrawFrameControl(Canvas.Handle, Rect, DFC_BUTTON, DFCS_BUTTONPUSH or DFCS_ADJUSTRECT); DrawFlags := DFCS_BUTTONCHECK or DFCS_ADJUSTRECT;// DFCS_BUTTONCHECK if Checked then DrawFlags := DrawFlags or DFCS_CHECKED; DrawFrameControl(Canvas.Handle, Rect, DFC_BUTTON, DrawFlags); end;
解决方案
问题核心在于:DrawColumnCell事件直接读取数据集的PickedUp字段值绘制,但本地数组的变更没有同步到绘制逻辑,且修改字段后未触发网格重绘。需做以下修改:
1. 修改绘制逻辑,优先读取本地数组状态
在dbgrid1DrawColumnCell中,先检查当前记录的DocumentID是否存在于本地更新数组中,存在则使用数组中的状态,否则用数据集字段值:
procedure Tform1.dbgrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var CurrentDocID: Integer; i: Integer; IsChecked: Boolean; begin if Column.FieldName = 'PICKEDUP' then begin CurrentDocID := dbgrid1.DataSource.DataSet.FieldByName('DOCUMENTID').AsInteger; // 默认使用数据集的字段值 IsChecked := Column.Field.AsBoolean; // 检查本地更新数组中是否有该记录的变更 for i := Low(DocumentsToUpdate) to High(DocumentsToUpdate) do begin if DocumentsToUpdate[i].DocumentID = CurrentDocID then begin IsChecked := DocumentsToUpdate[i].PickedUp; Break; end; end; DrawGridCheckBox(dbgrid1.Canvas, Rect, IsChecked); end; end;
2. 点击复选框后触发单元格重绘
修改dbgrid1CellClick事件,移除直接修改数据集字段的代码(避免意外触发数据集更新),添加单元格重绘代码:
procedure Tform1.dbgrid1CellClick(Column: TColumn); var DocumentID: Integer; PickedUp: Boolean; begin if Column.Field.DataType = ftBoolean then begin DocumentID := dbgrid1.DataSource.DataSet.FieldByName('DOCUMENTID').AsInteger; PickedUp := not Column.Field.AsBoolean; SetLength(DocumentsToUpdate, Length(DocumentsToUpdate) + 1); DocumentsToUpdate[High(DocumentsToUpdate)].DocumentID := DocumentID; DocumentsToUpdate[High(DocumentsToUpdate)].PickedUp := PickedUp; // 重绘当前单元格,立即显示变更 dbgrid1.InvalidateCell(Column.Index, dbgrid1.DataSource.DataSet.RecNo - 1); end; end;
3. 批量提交后刷新网格
在UpdateCheckedDocumentsPickedUp过程末尾添加网格刷新代码,确保提交后界面同步最新状态:
procedure Tform1.UpdateCheckedDocumentsPickedUp; var i: Integer; Bookmark: TBookmark; DocumentID: Integer; PickedUp: Boolean; begin if Length(DocumentsToUpdate) = 0 then Exit; Bookmark := dbgrid1.DataSource.DataSet.GetBookmark; dbgrid1.DataSource.DataSet.DisableControls; try for i := Low(DocumentsToUpdate) to High(DocumentsToUpdate) do begin DocumentID := DocumentsToUpdate[i].DocumentID; PickedUp := DocumentsToUpdate[i].PickedUp; if dbgrid1.DataSource.DataSet.Locate('DOCUMENTID', DocumentID, []) then begin dbgrid1.DataSource.DataSet.Edit; dbgrid1.DataSource.DataSet.FieldByName('PICKEDUP').AsBoolean := PickedUp; dbgrid1.DataSource.DataSet.Post; end; end; finally dbgrid1.DataSource.DataSet.GotoBookmark(Bookmark); dbgrid1.DataSource.DataSet.FreeBookmark(Bookmark); dbgrid1.DataSource.DataSet.EnableControls; end; SetLength(DocumentsToUpdate, 0); // 刷新整个网格 dbgrid1.Refresh; end;
补充优化:避免重复记录
多次点击同一复选框会在数组中添加重复记录,可优化dbgrid1CellClick事件,先检查数组中是否已有该记录,存在则更新状态而非追加:
procedure Tform1.dbgrid1CellClick(Column: TColumn); var DocumentID: Integer; PickedUp: Boolean; i: Integer; Found: Boolean; begin if Column.Field.DataType = ftBoolean then begin DocumentID := dbgrid1.DataSource.DataSet.FieldByName('DOCUMENTID').AsInteger; PickedUp := not Column.Field.AsBoolean; Found := False; // 检查数组中是否已有该记录,存在则更新 for i := Low(DocumentsToUpdate) to High(DocumentsToUpdate) do begin if DocumentsToUpdate[i].DocumentID = DocumentID then begin DocumentsToUpdate[i].PickedUp := PickedUp; Found := True; Break; end; end; // 不存在则追加 if not Found then begin SetLength(DocumentsToUpdate, Length(DocumentsToUpdate) + 1); DocumentsToUpdate[High(DocumentsToUpdate)].DocumentID := DocumentID; DocumentsToUpdate[High(DocumentsToUpdate)].PickedUp := PickedUp; end; dbgrid1.InvalidateCell(Column.Index, dbgrid1.DataSource.DataSet.RecNo - 1); end; end;
内容的提问来源于stack exchange,提问作者basti
相关产品推荐
相关产品推荐

