Delphi -与TButton相比,TImage的点击速度慢

46qrfjad  于 2023-11-18  发布在  其他
关注(0)|答案(1)|浏览(131)

我有一个表单,同时有一个TImage和一个TButton控件。我注意到TImage(快速点击!)对OnClick事件的响应速度似乎有点慢,所以我测量了一下。对于100多次点击(尽可能快地点击,尽可能保持每个控件的速度一致),我得到了指标:TButton:平均~105- 116 ms TImage:平均~220- 235 ms
我重复了几次类似的结果。为什么TImage处理点击的速度只有TButton的一半?从WM_LBUTTON_DOWN到OnClick事件的Windows消息队列的处理速度会更慢吗?如果点击在前一次点击的N ms内,也许它会吞下点击?
似乎没有什么在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;

字符串

xn1cxnb4

xn1cxnb41#

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事件,但 * 不会在鼠标按钮重新抬起时触发click事件 *。

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;


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

相关问题