【问题标题】:Delphi - WM_MOUSEWHEEL double processingDelphi - WM_MOUSEWHEEL 双重处理
【发布时间】:2017-08-13 02:26:58
【问题描述】:

我已经在我的应用程序的主窗口中添加了一个鼠标滚轮处理程序,它似乎可以工作,但不像我期望的 MSDN 在线文档那样工作。

根据 MSDN 的帮助,结果应该设置为零,表示消息已被处理,但如果我这样做,则例程会被调用两次。设置为非零值(在我的情况下为 –1)会导致它只被调用一次。

下面是一些说明问题的测试代码:

unit Mouse_Wheel_Testing;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Debug: TEdit;
    procedure MouseWheelHandler(var Message: TMessage); override;
    procedure FormCreate(Sender: TObject);
  private
    Call_Count: integer;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Call_Count := 0;
  Debug.Text := IntToStr(Call_Count);
end;

procedure TForm1.MouseWheelHandler(var Message: TMessage);
begin
  inc(Call_Count);
  Debug.Text := IntToStr(Call_Count);
  Message.Result := -1;
end;

end.

【问题讨论】:

  • 您是否有特定理由不使用现有事件的形式:OnMouseWheelOnMouseWheelDownOnMouseWheelUp
  • 是的。根据我的测试,仅当表单上支持鼠标滚轮滚动的控件具有焦点时才会触发 OnMouseWheel 事件。如果我可以确定它总是会触发,那么我很乐意使用现有的事件。
  • 那么,您想将所有鼠标滚轮消息定向到特定表单,即使另一个表单上的组件具有焦点,对吗?我看不出覆盖MouseWheelHandler() 将如何帮助您。 OTOH,TApplicationEvents 这样的procedure TForm19.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); begin if Msg.message = WM_MOUSEWHEEL then Msg.hwnd := Form19.Handle; end; OnMessage 处理程序会将所有鼠标滚轮消息定向到Form19(在这种情况下),您可以在其中使用OnMouseWheel() 事件。
  • 谢谢汤姆。由于某种原因,重载的 MouseWheelhandler 每次都会触发,但 OnMouseWheel 不会。我不知道为什么,因为我原以为他们会以类似的方式行事。感谢您提供有关应用程序事件处理程序的提示。如果需要,这给了我另一个选择。我仍然想知道为什么 OnMouseWheel 事件并不总是触发,以及为什么我在处理程序中返回的结果代码似乎与 MSDN 文档相反。与文档不符的事情总是让我感到紧张。
  • 我正在取得进展。我刚刚发现,如果我的表单上有一个诸如字符串网格之类的组件,并且该组件具有焦点,那么即使重写的 MouseWheelHandler 将始终触发,表单的 OnMouseWheel 事件也不会触发。很有趣。

标签: delphi winapi mousewheel


【解决方案1】:

从最初的问题和接下来的问题来看,这个问题分为两部分:

  • 如何将消息指示为已处理?
  • 为什么对消息处理程序的调用看起来如此不连贯,MouseWheelHandler 被调用了两次,OnMouseWheel 从未调用过,这取决于表单上的控件?

对于第一部分,VCL 代码假定Message.Result <> 0 表示已处理的消息。在您的测试中,您将Message.Result 设置为-1,因此它被处理。正如 Sertac Akyuz 所说,这可能不是 MSDN 定义的,而是 VCL 中的设计决策。

第二部分有两个基础。首先,WM_MOUSEWHEEL,系统生成的消息。第二,CM_MOUSEWHEEL,Delphi自己的C控制M消息在内部分发。

  • MouseWheelHandler 处理 WM_MOUSEWHEEL
  • OnMouseWheel(取决于结果和车轮增量OnMouseWheelUp/OnMouseWheelDown)响应CM_MOUSEWHEEL 触发的事件

滚动鼠标滚轮让 Windows 生成一个WM_SCROLLWHEEL,它在焦点窗口控件的WndProc 中接收。在这种情况下,这可能是TForm,很可能是TEdit,作为唯一可聚焦的控件。该消息将被发送到处理程序MouseWheelHandlerTControl.MouseWheelHandler 将尝试找到它所在的 ParentForm 并调用 TForm.MouseWheelHandler您的过程的第一个条目。这里——通常——Delphi 将PerformCM_MOUSEWHEEL 放在Form 的焦点控件上。但是,您覆盖的过程不会调用inherited 并禁用它。如果消息仍未处理,它将向上传播父链,表单的MouseWheelHandler 被第二次调用,第二次进入您的过程

如果生成了CM_MOUSEWHEEL 消息,它们将被分派到TControl.CMMouseWheel。在这里,根据DoMouseWheel 函数的结果,CM_MOUSEWHEEL 结果设置为 1,或者存在Parent 控件,在Parent 上执行相同的消息。 TStringGrid 例如将处理 MouseWheel 事件。 CM_MOUSEWHEEL 不会传递给父级,您的表单没有滚轮事件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    相关资源
    最近更新 更多