You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:37:36