简体   繁体   English

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

[英]Delphi - Rate of clicking TImage slow compared with TButton

I have a form with both a TImage and a TButton control. 我有一个同时具有TImage和TButton控件的表单。 I noticed the rate of responding to the OnClick event seemed a bit slow for the TImage (rapid clicking!) so I measured it. 我注意到,对于TImage(快速单击!),对OnClick事件的响应速度似乎有点慢,因此我对其进行了测量。 For 100+ clicks (and clicking as fast as I could, keeping the rate as consistent as I could for each control) I got the metrics: TButton: Average ~105-116ms TImage: Average ~220-235ms 对于100次以上的点击(并尽可能快地单击,使每个控件的速率保持一致),我得到了指标:TButton:平均〜105-116ms TImage:平均〜220-235ms

I repeated this a few times with similar results. 我重复了几次,结果相似。 Why is the TImage processing clicks about half the rate of the TButton? 为什么TImage处理的点击率约为TButton的一半? Could it be slower to process the Windows message queue from WM_LBUTTON_DOWN to the OnClick event? 从WM_LBUTTON_DOWN到OnClick事件处理Windows消息队列会更慢吗? Maybe it is swallowing Clicks if they are within N ms of the previous click? 如果它们在上一次点击的N毫秒内,可能会吞下点击?

There's doesn't seem to be anything in the properties of the TImage that affects this. 在TImage的属性中似乎没有任何影响它的东西。

Note: Using Delphi 7 and the standard VCL controls here, if that is relevant. 注意:如果需要,请在此处使用Delphi 7和标准VCL控件。

EDIT: Here is some example code demonstrating how I timed things: 编辑:这是一些示例代码,展示了我如何定时事情:

// 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;

The VCL source is your friend here. VCL来源是您的朋友。 As noted, this is caused by double click messages being sent by Windows when clicking fast enough to generate them. 如前所述,这是由Windows在足够快地单击以生成消息时发送的双击消息引起的。

Let's look at what happens when clicking fast enough to trigger a double click: 让我们看看单击足够快以触发双击时会发生什么:

Step 1 - Left mouse button goes down : 步骤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;

Step 2 - Left mouse button goes up . 步骤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;

Step 3 - Left mouse button goes down again. 步骤3- 鼠标左键再次下降。

This time, it's a double click! 这次是双击! Note that this is handling an entirely different message - a double click message from the OS, not a mouse down message. 请注意,这是在处理完全不同的消息-来自操作系统的双击消息,而不是鼠标按下消息。 The handler here still fires the MouseDown event, but does not prime the control to fire a click event when the mouse button comes back up. 此处的处理程序仍然会触发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;

Since a Button is a special TWinControl it receives the special BN_CLICKED message that is generated any time the button is clicked, regardless of whether it might be a double click or not. 由于Button是特殊的TWinControl因此它会收到在单击该按钮时生成的特殊BN_CLICKED消息,而不管它是否是双击。 Being a simple control it does a simple job and you therefore see twice as many click events from a button when clicking quickly (faster than the double-click rate). 作为一个简单的控件,它可以完成简单的工作,因此,在快速单击时(比双击速度更快),您从按钮上看到的单击事件是其两倍。

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

You can also note that, since a TButton will receive these special messages it is created without the csClickEvents option in its ControlStyle , so although it is also a TControl , the handling in the above steps used for the TImage (and other) controls does not apply (ie: priming for the Click in the WMLButtonDown handler). 您也可以注意到,因为TButton将收到它没有创建这些特殊的信息csClickEvents在其选项ControlStyle ,所以虽然它也是一个TControl ,用于在以上步骤处理TImage (等)控制不应用(即:为WMLButtonDown处理程序中的Click启动)。

As you have discovered, the OnMouseDown or OnMouseUp events will allow you to capture all such events in your TImage control, regardless of whether they should be treated as clicks or double clicks. 正如您所发现的, OnMouseDownOnMouseUp事件将使您能够捕获TImage控件中的所有此类事件,而不管它们应被视为单击还是双击。


Alternatively, if you don't care about your TImage processing double clicks you can set the control style as : 另外,如果您不关心双击TImage处理,则可以将控件样式设置为:

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

Here, in the TControl.WndProc : 在这里,在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;

You can see the double click events are transformed to simple mouse down events. 您可以看到双击事件已转换为简单的鼠标按下事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM