鼠标滚轮发送消息到错误的控件

5
我正在使用Windows XE2,TVirtualStringTreeTComboBox作为内置编辑器。
当我将TComboBox直接放在表单上时,在运行时我可以展开列表并使用鼠标滚轮向上或向下滚动列表项(如期望的那样)。但是,当TComboBoxTVirtualStringTree在运行时创建为内置编辑器时,即使新创建的组合框具有焦点,鼠标滚轮的WM_MOUSEWHEEL消息也会发送到树控件而不是组合框。
这是因为组合框下拉列表中的项目不会滚动。相反,组合框后面的树控件会滚动。组合框的固定部分随着树的移动而移动,但下拉列表与组合框的固定部分断开,并且不会移动(如所示)。
在两种情况下,TComboBox.Style都设置为csDropDownList。当组合框作为树的内置编辑器创建时,它是这样完成的:
FCBox := TComboBox.Create(TreeControl);
FCBox.Visible := False;
FCBox.Parent := TreeControl;
// ... add items to combo box ...
FCBox.Visible := True;
FCBox.SetFocus;
FCBox.DroppedDown := True;

无论鼠标悬停在哪里,都不会影响背景中的树形控件滚动,即使是直接悬停在组合框下拉列表中的项目上。唯一可以滚动组合框项目的方法是使用其滚动条。

是什么导致焦点控件的父级接收鼠标滚轮消息而不是控件本身(在此情况下,是TComboBox)?


我找到了这篇文章(http://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx),其中解释了消息是发送给控件的,如果它没有处理它,那么它会被发送到父级直到被处理。我现在正在查看`TVirtualStringTree`是否有任何更改`TComboBox`处理消息的方式。 - James L.
1个回答

1

VirtualTrees.pasTBaseVirtualTree类中包含以下声明:

private
  procedure CMMouseWheel(var Message: TCMMouseWheel); message CM_MOUSEWHEEL;

组件作者捕获了鼠标滚轮消息,这样他就可以先垂直滚动,然后再水平滚动。自定义代码是将鼠标滚轮消息发送到TVirtualStringTree而不是TComboBox的原因。我注释掉了他的代码,TComboBox下拉列表按预期滚动。
由于我真的不想删除TBaseVirtualTree代码,所以我创建了自己的TMyComboBox,并使用以下代码作为原地编辑器。现在,在下拉列表和树控件中滚动都可以正常工作。
interface

type
  TMyCombBox = class(TComboBox)
  private
    procedure CMMouseWheel(var Message: TCMMouseWheel); message CM_MOUSEWHEEL;
  end;

implementation

procedure TMyComboBox.CMMouseWheel(var Message: TCMMouseWheel);
begin
  if DoMouseWheel([], Message.WheelDelta, SmallPointToPoint(Message.Pos)) then
    Message.Result := 1;
end;

在传递给树形控件之前,捕获并将CM_MOUSEWHEEL消息交给TControl.DoMouseWheel()方法进行处理。


处理就地编辑器的消息是一种非常普遍的做法。如果你正在这样做,你将告诉 WM_MOUSEWHEEL 你已经处理过它了(通过你的就地编辑器),因此不会发生你所描述的任何事情。 - TLama
感谢@TLama - 问题在于消息从未发送到原地编辑器。相反,它立即发送到树控件,完全绕过了TComboBox。我不得不在自定义的TComboBox中重新捕获消息才能使其正常工作。我会将解决方案添加到我的答案中。 - James L.
消息已发送到组合框,但那个小家伙却无视了它(没有处理):-) 组合框中的项目更改由系统控制,除此之外组合框不使用鼠标滚轮,所以我不惊讶它在VCL中忽略了这条消息。 - TLama

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