【问题标题】:Delphi - Rate of clicking TImage slow compared with TButtonDelphi - 与 TButton 相比,点击 TImage 的速度较慢
【发布时间】:2015-08-14 20:13:04
【问题描述】:

我有一个包含 TImage 和 TButton 控件的表单。我注意到 TImage 对 OnClick 事件的响应速度似乎有点慢(快速点击!)所以我测量了它。对于 100 多次点击(并尽可能快地点击,使每个控件的点击率尽可能一致),我得到了指标: TButton:平均~105-116ms TImage:平均~220-235ms

我重复了几次,结果相似。为什么 TImage 处理点击率大约是 TButton 的一半?处理从 WM_LBUTTON_DOWN 到 OnClick 事件的 Windows 消息队列会更慢吗?如果它们在上一次点击的 N 毫秒内,它可能正在吞噬点击?

TImage 的属性中似乎没有任何影响这一点的东西。

注意:此处使用 Delphi 7 和标准 VCL 控件(如果相关)。

编辑:这是一些示例代码,演示了我如何计时:

// Define variables (in class definition)
m_dwBtnClicks, m_dwImgClicks: DWORD;
m_dwLastBtnClickTicks, m_dwLastImgClickTicks: DWORD;
m_fTotalBtnClicksTicks, m_fTotalImgClicksTicks: Single;

// Initialise variables (in form's OnCreate event)
m_dwBtnClicks := 0;
m_dwImgClicks := 0;

m_dwLastBtnClickTicks := 0;
m_dwLastImgClickTicks := 0;

m_fTotalImgClicksTicks := 0.0;
m_fTotalImgClicksTicks := 0.0;

// OnClick events
procedure TfrmQwerty.btnClick(Sender: TObject);
var
    dwTime: DWORD;
begin
    // TButton click!
    Inc(m_dwBtnClicks);
    dwTime := GetTickCount();
    if (m_dwLastBtnClickTicks > 0) then
        m_fTotalBtnClicksTicks := (m_fTotalBtnClicksTicks + (dwTime - m_dwLastBtnClickTicks));

    m_dwLastBtnClickTicks := dwTime;
end;

procedure TfrmQwerty.imgClick(Sender: TObject);
var
    dwTime: DWORD;
begin
    // TImage click!
    Inc(m_dwImgClicks);
    dwTime := GetTickCount();
    if (m_dwLastImgClickTicks > 0) then
        m_fTotalImgClicksTicks := (m_fTotalImgClicksTicks + (dwTime - m_dwLastImgClickTicks));

    m_dwLastImgClickTicks := dwTime;
end;

// Some TTimer::OnTimer event to update the results on-screen
procedure TfrmQwerty.OnTextEntryTimer(Sender: TObject);
var
    fTime: Single;
begin
    // Stop the timer
    TextEntryTimer.Enabled := False;

    if (m_dwBtnClicks > 1) then
        begin
        fTime := m_fTotalBtnClicksTicks / m_dwBtnClicks;
        lblButtonClicks.Caption := Format('BtnClicks = %d [Avg = %.3fms]', [
            m_dwBtnClicks, fTime]);
        end;

    if (m_dwImgClicks > 1) then
        begin
        fTime := m_fTotalImgClicksTicks / m_dwImgClicks;
        lblImageClicks.Caption := Format('ImgClicks = %d [Avg = %.3fms]', [
            m_dwImgClicks, fTime]);
        end;

    // Restart the timer
    TextEntryTimer.Enabled := True;
end;

【问题讨论】:

  • 你在计时什么?请展示一个简短的演示程序,使我们能够执行完全相同的计时。
  • TButton 使用BN_CLICKED 系统通知来触发OnClick 事件,而TImage 只跟踪鼠标向下/鼠标向上事件对,所以我不会对存在差异感到惊讶。跨度>
  • @DavidHeffernan:好的,添加了一些示例代码来显示事情是如何计时的。没有什么花哨。当我注意到很大的不同时,正在用 QWERTY 风格的屏幕键盘上的 TImage 替换 TButton。
  • @David,可能回答这个话题的问题是,为什么TButton 没有双击事件?我认为它根本不需要等待事件是单击还是双击的决定(由于使用的按钮样式,它不会收到BN_DBLCLK 通知)。但这只是一个疯狂的猜测。我现在没有时间进行更深入的调查。
  • @Alain - 不,我说的是控制风格。 Image1.ControlStyle := Image1.ControlStyle - [csDoubleClicks];

标签: delphi delphi-7


【解决方案1】:

VCL 源是您的朋友。如前所述,这是由 Windows 发送的双击消息造成的,当单击速度足以生成它们时。

让我们看看当点击速度足以触发双击时会发生什么:

第 1 步 - 鼠标左键按下

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
  SendCancelMode(Self);
  inherited;
  if csCaptureMouse in ControlStyle then
    MouseCapture := True;
  if csClickEvents in ControlStyle then  // !! Note here
    Include(FControlState, csClicked);   //    Storing that we've been clicked
  DoMouseDown(Message, mbLeft, []);
end;

第 2 步 - 鼠标左键向上

procedure TControl.WMLButtonUp(var Message: TWMLButtonUp);
begin
  inherited;
  if csCaptureMouse in ControlStyle then MouseCapture := False;
  if csClicked in ControlState then      // !! Note here
  begin                                  //    Firing CLICK event primed  
    Exclude(FControlState, csClicked);   //    from the method above
    if ClientRect.Contains(SmallPointToPoint(Message.Pos)) then
      Click;
  end;
  DoMouseUp(Message, mbLeft);
end;

第 3 步 - 鼠标左键再次按下。

这次是双击!请注意,这是处理完全不同的消息 - 来自操作系统的双击消息,而不是鼠标按下消息。此处的处理程序仍会触发MouseDown 事件,但不会在鼠标按钮恢复时触发控件以触发单击事件

procedure TControl.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
  SendCancelMode(Self);
  inherited;
  if csCaptureMouse in ControlStyle then MouseCapture := True;
  if csClickEvents in ControlStyle then DblClick;
  DoMouseDown(Message, mbLeft, [ssDouble]);
end;

由于 Button 是一个特殊的 TWinControl,它会在任何时候单击该按钮时收到特殊的 BN_CLICKED 消息,无论它是否可能是双击。作为一个简单的控件,它完成了一项简单的工作,因此在快速单击时您会看到两倍于按钮的单击事件(比双击速度快)。

procedure TCustomButton.CNCommand(var Message: TWMCommand);
begin
  if Message.NotifyCode = BN_CLICKED then Click;
end;

您还可以注意到,由于TButton 将接收这些特殊消息,因此它的创建在其ControlStyle 中没有csClickEvents 选项,因此尽管它也是TControl ,上述步骤中用于TImage(和其他)控件的处理不适用(即:在WMLButtonDown 处理程序中为Click 启动)。

正如您所发现的,OnMouseDownOnMouseUp 事件将允许您在 TImage 控件中捕获所有此类事件,无论它们应被视为单击还是双击。


或者,如果您不关心TImage 处理双击,您可以将控件样式设置为:

 Image1.ControlStyle := Image1.ControlStyle - [csDoubleClicks];

这里,在TControl.WndProc

if not (csDoubleClicks in ControlStyle) then
  case Message.Msg of
    WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
      Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
  end;

您可以看到双击事件被转换为简单的鼠标按下事件。

【讨论】:

  • 点击事件发生在鼠标向上而不是鼠标向下。
  • @DavidHeffernan 我不认为我另有说法。除非你的意思是双击,这肯定是在鼠标按下时发生的。
  • 您说:正如您所发现的,OnMouseDown 事件将允许您捕获所有鼠标按下事件......无论它们应该被视为单击还是双击。
  • 我认为这是一个小错误,而 OnMouseUp 是有意的。意思很清楚,我想……
  • @DavidHeffernan 是的,我改变了措辞,以防你反对——这很冒险,因为双击是 mousedowns 而点击是 mouseups。希望现在更清楚了。
猜你喜欢
  • 2018-07-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多