【问题标题】:Detect "whitespace" click in a listbox检测列表框中的“空白”单击
【发布时间】:2014-09-22 20:12:05
【问题描述】:

我有一个包含可变数量项目的高列表框。它不会总是满的。我知道用户何时不通过此代码选择项目:

if ( lstbox.ItemIndex = -1 ) then
  ShowMessage('here');

但是当我选择一个项目然后我单击列表框的“空白”时,这不起作用。我如何发现这种情况?

【问题讨论】:

  • 我最近在 XE5 中注意到了这一点。我很好奇这是否是 TListboxes 中行为的一个相对较新的变化,因为我发誓过去是,如果您单击空格,无论是否选择了一个项目,.ItemIndex 的值都会是-1。现在,如果已选择项目,它会忽略单击。

标签: delphi


【解决方案1】:

您可以通过多种方式做到这一点。一种是在 OnMouseDown 事件中使用该事件的 XY 参数,它们是列表框中的客户端坐标用户点击:

procedure TMyForm.ListBox1MouseDown(Sender: TObject;
                                    Button: TMouseButton;
                                    Shift: TShiftState;
                                    X,  Y: Integer);
begin
  if TListbox(Sender).ItemAtPos(Point(X, Y), TRUE) <> -1 then
    // item was clicked
  else
    // 'whitespace' was clicked
end;

但这不会影响任何 OnClick 事件中的行为。如果您需要在 OnClick 中执行此测试,那么您需要在执行相同测试之前获取鼠标位置并将其转换为列表框客户端坐标:

procedure TMyForm.ListBox1Click(Sender: TObject);
var
  msgMousePos: TSmallPoint;
  mousePos: TPoint;
begin
  // Obtain screen co-ords of mouse at time of originating message
  //
  // Note that GetMessagePos returns a TSmallPoint which we need to convert to a TPoint
  //  in order to make further use of it

  msgMousePos := TSmallPoint(GetMessagePos);  

  mousePos := SmallPointToPoint(msgMousePos);
  mousePos := TListbox(Sender).ScreenToClient(mousePos);

  if TListbox(Sender).ItemAtPos(mousePos, TRUE) <> -1 then
    // item clicked
  else
    // 'whitespace' clicked
end;

注意:GetMessagePos() 获取最近观察到的鼠标消息(在这种情况下应该是发起 Click 事件的消息)时的鼠标位置)。但是,如果直接调用 Click 处理程序,则 GetMessagePos() 返回的鼠标位置可能与处理程序中的处理几乎没有相关性或没有相关性。如果任何此类直接调用可能会明智地利用当前鼠标位置,则可以使用 GetCursorPos() 获得。

GetCursorPos() 使用起来也更加直接,因为它直接在 TPoint 值中获取鼠标位置,避免了从 TSmallPoint:

GetCursorPos(mousePos);

无论哪种方式,您的处理程序以任何方式依赖于鼠标位置这一事实使得直接调用此事件处理程序存在问题,因此如果这是一个考虑因素,那么您可能希望将事件处理程序中的任何与位置无关的响应隔离到一个方法中可以在需要时显式调用,与鼠标与控件的交互无关。

【讨论】:

  • 还有全局鼠标的CursorPos。
  • 应该是 GetMessagePos 而不是 GetCursorPos。与 GetKeyState 和 GetAsyncKeyState 的区别相同。但请记住,由于鼠标事件以外的某种原因触发 OnClick 的可能性。
  • 除非直接调用了 OnClick 事件,在这种情况下,消息循环观察到的鼠标位置可能非常不正确,因为 GetCursorPos() 极有可能会稍微偏离。当实现对消息的直接响应时,GetMessagePos() 是合适的,但在 VCL 事件处理程序中,您没有这种确定性。
  • 如果 OnClick 在鼠标事件之外触发,光标位置没有意义。这是我评论的第二部分。如果响应鼠标事件触发 OnClick,则应使用 GetMessagePos。但永远不要 GetCursorPos。您希望程序响应您单击的位置,而不是程序响应时您必须到达的位置。 Rob 的回答很好地解释了这一点:stackoverflow.com/questions/826914/…
  • OP 应该考虑使用 GetMessagePos() 并且这些 cmets 应该让他们这样做。以防万一,我将更新答案以突出显示该选项。
猜你喜欢
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多