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

Delphi中如何禁用与重新启用TAdvStringGrid的鼠标点击?

解决Delphi中TStringGrid/TAdvStringGrid多线程填充时阻止鼠标点击的问题

针对你遇到的多线程填充网格时,鼠标点击导致数据错乱的问题,直接设置Enabled := False会让控件变灰影响视觉体验,这里有几个更贴合需求的解决方案:

方案1:标志位+鼠标事件拦截

你可以在Form中设置一个布尔标记,标记当前是否处于数据填充状态,然后在网格的鼠标点击事件里判断这个标记,若处于填充状态就直接终止事件处理。

代码修改步骤:

首先在Form类中添加私有变量和事件声明:

private
  FIsPopulating: Boolean; // 标记是否正在填充数据
  FActiveThreads: Integer; // 统计活跃线程数
  procedure StringGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  procedure DecrementThreadCount;

然后在Form初始化时绑定事件并初始化变量:

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.OnMouseDown := StringGridMouseDown;
  FIsPopulating := False;
  FActiveThreads := 0;
end;

实现鼠标点击拦截逻辑:

procedure TForm1.StringGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if FIsPopulating then
  begin
    // 阻止后续鼠标点击处理
    Abort;
    // 可选:给用户提示
    // ShowMessage('数据加载中,请稍候');
  end;
end;

修改按钮点击事件,启动线程前标记填充状态:

procedure TForm1.Button1Click(Sender: TObject);
begin
  FIsPopulating := True;
  FActiveThreads := 8; // 对应你创建的8个线程数
  TThread_populate_stringgrid.Create('Andrei', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Matei', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Iulia', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Petru', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Gheorghe', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Tudor', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Cineva', form1.StringGrid1);
  TThread_populate_stringgrid.Create('Altcine', form1.StringGrid1);
end;

实现线程计数递减逻辑,所有线程完成后恢复鼠标交互:

procedure TForm1.DecrementThreadCount;
begin
  Dec(FActiveThreads);
  if FActiveThreads = 0 then
    FIsPopulating := False; // 所有填充完成,允许鼠标操作
end;

最后修改线程的Execute方法,完成后通知主线程更新计数:

procedure TThread_populate_stringgrid.Execute;
begin
  Synchronize(
    procedure
    begin
      f_stringgrid_to_populate.cells[0,0]:='DATE';
      f_stringgrid_to_populate.cells[1,0]:='NAME';
      f_stringgrid_to_populate.cells[2,0]:='ADRESS';
      f_stringgrid_to_populate.cells[3,0]:='CITY';
      f_stringgrid_to_populate.cells[4,0]:='COUNTRY';
      f_stringgrid_to_populate.Cols[0].Add(FormatDatetime('dd-mm-yyyy hh:mm:ss', Now));
      f_stringgrid_to_populate.Cols[1].Add(f_name);
      f_stringgrid_to_populate.Cols[2].Add('58 RED ROAD');
      f_stringgrid_to_populate.Cols[3].Add('ENGLAND');
      f_stringgrid_to_populate.Cols[3].Add('UK');
    end
  );
  // 线程结束后通知主线程递减计数
  Synchronize(procedure begin TForm1(form1).DecrementThreadCount; end);
end;

方案2:子类化控件拦截鼠标消息

如果不想依赖事件绑定,可以通过子类化StringGrid,直接拦截所有鼠标相关的系统消息,在填充期间忽略这些消息。

代码示例:

在Form类中添加变量保存原窗口过程:

private
  FOrgGridWndProc: TWndMethod;
  FIsPopulating: Boolean;
  FActiveThreads: Integer;
  procedure GridWndProc(var Msg: TMessage);
  procedure DecrementThreadCount;

在Form创建和销毁时处理子类化:

procedure TForm1.FormCreate(Sender: TObject);
begin
  FOrgGridWndProc := StringGrid1.WindowProc;
  StringGrid1.WindowProc := GridWndProc;
  FIsPopulating := False;
  FActiveThreads := 0;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // 恢复原窗口过程
  StringGrid1.WindowProc := FOrgGridWndProc;
end;

实现自定义窗口过程拦截鼠标消息:

procedure TForm1.GridWndProc(var Msg: TMessage);
const
  // 需要拦截的鼠标消息列表
  MouseMessages: array[0..4] of Cardinal = (WM_LBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_MOUSEWHEEL);
var
  i: Integer;
begin
  if FIsPopulating then
  begin
    // 检查是否是鼠标消息,是则直接返回不处理
    for i := Low(MouseMessages) to High(MouseMessages) do
    begin
      if Msg.Msg = MouseMessages[i] then
        Exit;
    end;
  end;
  // 非拦截消息交给原窗口过程处理
  FOrgGridWndProc(Msg);
end;

后续的线程计数和标志位逻辑和方案1一致即可。

方案3:用EnableWindow保持视觉状态(快速实现)

如果只是不想让控件变灰但需要阻止交互,可以调用Windows API的EnableWindow,它会禁用控件输入但保留原有外观(和Enabled := False的灰化效果不同)。

使用示例:

// 开始填充时禁用输入
EnableWindow(StringGrid1.Handle, False);
// 所有线程完成后恢复输入
EnableWindow(StringGrid1.Handle, True);

你可以结合方案1的线程计数逻辑,在Button1Click中调用禁用代码,在DecrementThreadCount中当计数归零时调用恢复代码。


另外提醒下:你当前的多线程代码中,多个线程同时向Cols添加数据,虽然Synchronize会把操作放到主线程执行,但建议统一管理填充顺序,或者用临界区确保数据操作的原子性,进一步避免潜在冲突。

内容的提问来源于stack exchange,提问作者user2858981

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:04:20