如何确定鼠标指针是否在控件内部

16
我正在向TScrollBox添加鼠标滚轮移动支持(使用FormMouseWheel过程),我需要确定鼠标是否在组件内。
基本上,我需要确定鼠标是否在TScrollBox内,以便相应地处理滚动代码。
有什么好的方法吗?
编辑:这里是代码(包括答案),希望对其他人有所帮助:
   procedure TForm1.FormMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
  Msg: Cardinal;
  Code: Cardinal;
  I, ScrollLines: Integer;

  ScrollBoxCursosPos: TPoint;
begin
  //position of the mouse cursor related to TScrollBox
  ScrollBoxCursosPos := ScrollBox1.ScreenToClient(Mouse.CursorPos);

  if (PtInRect(ScrollBox1.ClientRect, ScrollBoxCursosPos)) then
  begin
    Handled := True;
    If ssShift In Shift Then
      msg := WM_HSCROLL
    Else
      msg := WM_VSCROLL;

    If WheelDelta < 0 Then
      code := SB_LINEDOWN
    Else
      code := SB_LINEUP;

    ScrollLines:= Mouse.WheelScrollLines * 3;
    for I:= 1 to ScrollLines do
      ScrollBox1.Perform(Msg, Code, 0);
    ScrollBox1.Perform(Msg, SB_ENDSCROLL, 0);
  end;
end;
3个回答

29

Mouse.CursorPos 返回鼠标在屏幕上的坐标。你可以通过调用控件的ScreenToClient方法将其转换为相对于控件的“客户端”坐标。

所以你需要写出类似以下代码:

var
  MyPoint : TPoint;
begin
  MyPoint := ScrollBox1.ScreenToClient(Mouse.CursorPos);
  if PtInRect(ScrollBox1.ClientRect, MyPoint) then
  begin
    // Mouse is inside the control, do something here
  end;
end;
那将让您知道它是否在控件内。从外观上看,您正在使用鼠标滚轮实现滚动?如果是这样,请不要忘记调用SystemParametersInfoSPI_GETWHEELSCROLLLINES或者在您的Delphi版本中可能会找到Mouse.WheelScrollLines以了解每个鼠标滚轮增量应滚动多少行。这对您的应用程序可能取决于您在滚动框中放置的内容。如果您计划实现中键拖动滚动(我在这里推测,这已经超出了您所问的问题),您可能希望获取鼠标离开控件或窗体后的鼠标事件,直到用户释放按钮为止。如果是这样,请查看SetCaptureReleaseCapture这篇文章。(那篇文章使用这些内容来查看鼠标是否在控件(在那里,窗体)之上,尽管我认为我上面写的代码是解决该特定问题的更好方案——关键是它们可以方便地获取鼠标信息,即使鼠标不在您的窗体或控件之上。)(编辑:我刚刚注意到Delphi 2010的TMouse有包装这些API调用的属性,WheelScrollLinesCapture。我不确定它们是最近添加的还是我以前没有注意到它们,但是假设它们是新的,并且因为您没有说您正在使用哪个版本的Delphi,所以我保留上面的文本和WinAPI引用。如果您使用的是最近的版本,请查看TMouse文档。)

1
太好了,我已经到达了ScreenToClient部分,但不确定是否得到了正确的值。感谢您的帮助。 - smartins

1
我正在使用相同的方法来使用鼠标滚动我的滚动框。这是表单 MouseWheel 事件的事件处理程序。如果您在滚动时按下 Shift 键,它将水平滚动:
procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
  Msg: Cardinal;
  Code: Cardinal;
  I, ScrollLines: Integer;    
begin
  if IsCoordinateOverControl(MousePos, ScrollBox1) then
  begin
    Handled := True;
    If ssShift In Shift Then
      Msg := WM_HSCROLL
    Else
      Msg := WM_VSCROLL;

    If WheelDelta < 0 Then
      Code := SB_LINEDOWN
    Else
      Code := SB_LINEUP;

    ScrollLines := Mouse.WheelScrollLines * 3;
    for I := 1 to ScrollLines do
      ScrollBox1.Perform(Msg, Code, 0);
    ScrollBox1.Perform(Msg, SB_ENDSCROLL, 0);
  end;
end;

你可以使用这个函数来检查鼠标的屏幕坐标是否在你的控件上方:
function IsCoordinateOverControl(screenCoordinate: TPoint; control: TControl): Boolean;
var
  p: TPoint;
  r: TRect;
begin
  Result := False;
  p := control.ScreenToClient(screenCoordinate);
  r := Rect(0, 0, control.Width, control.Height);
  if PtInRect(r, p) then
    Result := True;
end;

0

我的 Delphi 知识有点生疏,但是不应该有 MouseEnter、MouseLeave 事件吗?快速谷歌搜索显示了 this。这对你有帮助吗?


我需要使用的过程是FormMouseWheel,因为我需要响应鼠标滚轮的移动,而不是进入和离开事件。 - smartins
是的,但您可以使用这些事件来了解鼠标何时进入或离开(=>在内部)滚动框。 - Thorsten Dittmar
mouseenter/leave的问题在于这些事件也会为子控件触发。因此,我支持David M的解决方案。 - utku_karatas

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