【问题标题】:Why WM_LBUTTONUP message dispear in Delphi?为什么 WM_LBUTTONUP 消息在 Delphi 中消失?
【发布时间】:2014-08-11 18:11:55
【问题描述】:

MouseDownMouseUp 在我这样写时会运行:

procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    Label1.Caption:='ddddddd';
end;

procedure TForm1.Edit1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    ShowMessage('mouseup');
end;

但是当我这样写的时候,WM_LBUTTONUP 消失了,Edit1MouseUp 也不会运行,为什么?

procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    ShowMessage('mousedown');
end;

procedure TForm1.Edit1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    ShowMessage('mouseup');
end;

【问题讨论】:

    标签: delphi message


    【解决方案1】:

    当您调用ShowMessage 时,会显示一个模式窗口。在模式窗口关闭之前,对ShowMessage 的调用不会返回。模态窗口运行它自己的消息循环,并吃掉鼠标向上的消息。因此,已经在消息队列中或即将放入消息队列中的WM_LBUTTONUP 实际上是由消息框而不是您的 Delphi 表单处理的。

    该消息是如何处理的?这要看情况。如果消息是在显示模式窗口之前发布的,那么它将被分派到被禁用的所有者窗口。如果它在模态窗口显示后发布,那么它可能会被分派到模态窗口。

    这是通过鼠标向上而不是鼠标向下调用动作的原因之一。也许您还没有注意到这一点,但请尝试单击任何常见应用程序中的按钮,并注意仅当鼠标上升时才会发生响应。实际上,如果您在按钮上按下鼠标,将光标从按钮上移开,然后松开按钮,则不会触发该操作。

    现在尝试对您的第二个代码示例进行类似的操作。在编辑控件中按下鼠标但不要立即释放它。请注意,鼠标向下的结果是现在显示了一个模式窗口。它运行自己的消息循环并且您的表单被禁用。现在释放鼠标按钮。显然,WM_LBUTTONUP 消息将被模态窗口的消息循环从队列中拉出。

    【讨论】:

    • 你的回答也很有趣。但我认为最重要的原因是表单被禁用,而不是模式吃掉了鼠标向上消息。因为即使它有自己的消息循环,这个消息也应该通过它的句柄发送(dispatchMessage)给所有者,不是吗?但是禁用表单不会自动接收或忽略消息?
    • 比这更复杂。如果对话框显示在光标下,然后您松开鼠标,那么消息会去哪里?有多种情况需要考虑:在显示对话框之前发布消息,在显示对话框之后发布消息是两种明显的情况。
    • @user - 是的,一个禁用的窗口就是这样,它不接收鼠标/键盘输入。 - David - 在问题的场景中,无法在显示对话框之前发布鼠标向上消息,因为对话框显示在鼠标按下事件中。
    • 对 Sertac Akyuz,我终于明白了。每个禁用表单,或创建一个新的消息循环谁吃消息,都将起作用。看看这个:procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);开始断言(GetCapture = Edit1.Handle);释放捕获;表格2.显示;结束;
    • @stlcours - 当我测试时,它不会吃任何东西,鼠标向上被触发。
    【解决方案2】:

    在第二种情况下,编辑控件永远不会发布WM_LBUTTONUP,因此不会触发OnMouseUp 事件。

    当您在OnMouseDown 中调用ShowMessage 时,会启动一个对话框。这不仅会从编辑控件中释放鼠标捕获,还会禁用表单。对于这两种情况,消息无处可发布:不能发布到带有捕获的窗口,也不能发布到光标下的窗口(请参阅documentation)。

    您可以使用此代码模拟行为,而不显示消息:

    procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      Assert(GetCapture = Edit1.Handle);
      ReleaseCapture;
      Enabled := False;
    end;
    
    procedure TForm1.Edit1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      ShowMessage('mouseup'); // will not fire.
    end;
    


    道德是:不要使用ShowMessage 来调试涉及激活/聚焦的情况。由于在这种情况下可能不太明显,因此通常根本不要将其用作调试工具。

    【讨论】:

    • 如果在线程处理鼠标按下之前释放鼠标,则消息可能被放入队列
    • @David - 线程首先处理鼠标按下,这个处理导致鼠标按下消息不被发布。
    • 线程在轮到抽出队列时处理消息。到那时鼠标是否已经被释放是不确定的。
    • @David - 如果应用程序在单击编辑时忙于执行代码,可能会这样。但是,答案回答了问题,当您在应用程序空闲时单击编辑时,消息未发布
    • 我并不想对此大惊小怪。只是为了强调输入消息的异步性质。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    相关资源
    最近更新 更多