当我执行Form1的OnDblClick事件打开Form2时,它会触发Form2的OnCellClick事件,而不必在form2网格上单击。

7

事件表单1:

procedure TForm1.Panel1DblClick(Sender: TObject);
begin
  TForm2.Create(Self).ShowModal;
end;

事件表单 2:

procedure TForm2.DBGrid1CellClick(Column: TColumn);
begin
  ShowMessage('Test');
end;

我应该怎么做才能避免fom2的onCellClick事件?


我猜测DB Grid在物理上与面板处于同一位置(重叠),因此它接收Mouse Up操作。是这样吗?另外,为什么要使用TForm2.create?在我看来,您将创建TForm2的多个副本,这可能不是很好。 - Dsm
没错。在我的应用程序中,我有一个问题,即在没有点击DBGrid的情况下,单击面板并触发onCellClick事件,我需要忽略onCellClick事件。 - Luiz Felipe Heemann
你的代码也存在泄漏实例的问题。也就是说,TForm2 实例直到 TForm1 实例死亡才被销毁。 - David Heffernan
@MartynA 你必须发布消息而不是发送它。 - David Heffernan
2
当你在鼠标抬起事件中实现点击时,会触发CellClick响应WM_LBUTTONUP。而在第二个按钮向下时,会发布WM_LBUTTONDBLCLK。我已经看到过多次建议处理鼠标抬起来实现点击。点击是不同的,它是按下/捕获/释放。这是一个VCL设计错误。 - Sertac Akyuz
显示剩余4条评论
3个回答

4
操作系统在左键第二次按下时会发布一个WM_LBUTTONDBLCLK消息。当您在此处执行ShowModal调用时,应用程序无法在对话框显示之前处理尚未发布的WM_LBUTTONUP消息。由于TDBGrid在处理WM_LBUTTONUP消息时触发OnCellClick事件,并且该消息恰好被发布到网格中,因为模态窗体现在是活动窗口,所以您会遇到问题。
网格的行为有点被记录

当用户释放鼠标时,发生在网格单元格中的其中一个。

虽然可以说它应该提到您甚至不必按下鼠标按钮...
这是一个不幸的设计决策,这不是点击的工作方式。想象一下在一个单元格上按下按钮并在另一个单元格上释放按钮。不应该触发任何OnCellClick。当前的行为相当令人困惑,如果您在有效单元格上释放按钮而不是在空白区域上,则事件将为您按下按钮的单元格触发。
正如您所发现的,您甚至可以通过在不同的表单上按下按钮并在该表单上的网格单元格上释放它来触发事件。在这种情况下,事件将为当前选定的单元格触发,并且鼠标位置根本不起作用。我的看法是OnCellClick是一团糟。

您可以使用kobik的答案解决问题。如果由于某种原因在第二次按下鼠标按钮时按住该按钮任意时间段,则以下解决方案将失败。
像问题评论中建议的那样,发布一个自接收消息以延迟对话框的显示不起作用,因为发布的消息比输入消息具有更高的优先级。有关GetMessage的详细信息,请参阅文档
如果您遵循链接,您会注意到定时器方法也可以像问题评论中建议的那样工作。与评论所建议的不同,计时间隔无关紧要,因为WM_TIMER消息具有最低优先级。这是一件好事,使其成为一种故障安全方法。
我想将计时器放在模态对话框上,因为它拥有问题控件。
procedure TForm2.FormCreate(Sender: TObject);
begin
  DBGrid1.Enabled := False;
  Timer1.Interval := 1;
  Timer1.Enabled := True;
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
  DBGrid1.Enabled := True;
  Timer1.Enabled := False;
end;

间隔为1可能不够,至少在我的测试中不够用(100个工作良好)。赞同分析结果。我本以为使用 PeekMessage + PM_REMOVE 可以完成任务,但似乎消息队列中没有东西? - kobik
@kobik - 谢谢。然而,你的情况证明了我的分析完全不正确。在窗体的OnCreate中使用PeekMessage可以正常工作。这就是为什么1毫秒定时器也能正常工作的原因。你会在一段时间后(约10-100毫秒)在队列中找到消息。我想不出任何原因为什么它会以这种方式工作。 - Sertac Akyuz
1
@kobik - 搞定了,答案就在我发布的回答里。你可以在任何时候松开鼠标按钮。显然你的鼠标有点慢。现在我得想办法如何挽救这个问题... - Sertac Akyuz

3

@Sertac提供了一个很好的解释。

我将尝试通过创建一个中间类来修复TDBGrid,例如:

type
  TDBGrid = class(DBGrids.TDBGrid)
  protected
    FDown: Boolean;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  end;

  TForm2 = class(TForm)
    ...
    DBGrid1: TDBGrid;
    ...
  end;

implementation

procedure TDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  FDown := True;
  try
    inherited;
  except
    FDown := False;
    raise;
  end;
end;

procedure TDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if FDown then
  try
    inherited;
  finally
    FDown := False;
  end;
end;

FDown标志仅表示在MouseDown消息之后只能跟随MouseUp消息。
经过我的简单测试,我没有注意到任何影响,但是可能会有影响。


0
你尝试在DblClick处理程序中执行Application.ProcessMessages()了吗?
procedure TForm1.Panel1DblClick(Sender: TObject);
begin
  Application.ProcessMessages;
  TForm2.Create(Self).ShowModal;
end;

1
我尝试了这个方法,似乎可以工作,尽管我在答案中没有提到。但它将遭受与我的答案中的解决方案相同的非确定性按钮释放时间问题 - 对于第二次按下,请将鼠标按钮按住更长时间以使其失败,即点击-释放-点击-按住-Application.ProcessMessages(无需处理)-释放(消息已发布)。 - Sertac Akyuz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接