设计时无法单击自定义控件

4
我正在使用Delphi XE2构建一个自定义控件(继承自TCustomControl),在设计时,我无法点击它们。我知道这与鼠标捕获有关,并且需要在设计时和运行时以不同的方式处理鼠标事件,但我不知道如何正确地适应这种情况。换句话说,虽然我可以想到许多解决方法,但我无法确定哪种是正确的(或最有效的)。
我相信这个问题肯定有一些非常简单的标准解决方案,很可能利用了ControlStyleCreateParams,但我不知道具体是什么。
在这个特定的控件中(我没有看到这个问题的模式),我捕获包括WM_NCHITTESTWM_LBUTTONDOWN等消息。在设计时,该控件像运行时一样100%活动,在点击时,它实际上执行的是运行时代码。
我感觉问题出在我的命中测试消息处理程序中,所以这是那段代码(一些东西被重新命名):
procedure TMyCustomControl.WMNCHitTest(var Message: TWMNCHitTest);
var
  P: TPoint;
  Poly: TPoints;
  X: Integer;
  I: TMyCollectionItem;
  Ch: Bool; //Need to improve invalidation
begin
  Ch:= False;
  P:= ScreenToClient(Point(Message.Pos.X, Message.Pos.Y));
  for X := 0 to Items.Count - 1 do begin
    I:= Items[X];
    Poly:= I.Points;
    FMouseIndex:= -1;
    FMouseState:= bmNone;
    if PointInPolygon(P, Poly) then begin //checks if point is within polygon
      FMouseIndex:= X;
      FMouseState:= bmHover;
      Ch:= True;
      Break;
    end;
  end;
  if Ch then Invalidate;
end;

此外,我的控件构造函数(已剥离):

constructor TMyCustomControl.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle:= ControlStyle - [csDesignInteractive];
end;

更具体地说,这个控件是一个水平(或垂直)的项目列表,或者说是箭头。每个项目都被绘制成一个多边形,依次排列,每个项目周围的空间被视为无效(背景)。当鼠标悬停在一个项目上时,我会突出显示该项目(并执行其他对该项目的内部引用)。我还计划实现对单个列表项的焦点控制。 - Jerry Dodge
2个回答

6
当然,您是正确的。在WM_NCHITTEST处理程序中,您没有返回任何内容。当调用处理程序时,您的Mmessage.Result是0(HTNOWHERE),并且您没有对其进行任何其他赋值。
要么在某个位置调用inherited,要么实现自己的逻辑并返回(将Message.Result设置为)HTCLIENT以表示您控件内部的点。
如果这已经是运行时所需的行为,则可以包括设计时检查(但我猜您应该有一个理由进行所有计算):
if csDesigning in ComponentState then
  Msg.Result := HTCLIENT;

+1 csDesigning 起了作用 - 我知道问题大概是这样,但一开始我在 ControlState 中查找而不是 ComponentState,所以一直找不到答案。谢谢! - Jerry Dodge

5

在设计时支持鼠标交互的官方方法是对 CM_DESIGNHITTEST 消息响应非零结果。然后组件将接收正常的鼠标消息。


据我所知,只有在控件对 WM_NCHITTEST 返回 HTCLIENT 时才会发送 CM_DESIGNHITTEST。顺便说一句,通过查看 VCL 源代码,我并没有完全理解它的工作原理,而且也没有文档记录。我为一个测试控件返回了0,但是没有观察到任何不同的行为。 - Sertac Akyuz
你不需要处理 WM_NCHITTEST,让默认的处理程序为你管理它。大多数控件不需要支持设计时交互,因此它们不会响应 CM_DESIGNHITTEST,但是一些本地控件会响应,例如允许可视化调整列宽而不使用对象检查器的控件。 - Remy Lebeau

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