MouseDown上的游标不会改变

zig*_*zig 7 delphi

我有一个有2个事件的TPanel:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Panel1.Cursor := crSizeAll;
end;

procedure TForm1.Panel1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Panel1.Cursor := crDefault;
end;
Run Code Online (Sandbox Code Playgroud)

当我单击面板(MouseDown事件)时,光标不会更改为crSizeAll.我做错了什么,如何解决这个问题?

Dim*_*ima 8

你做得很好.捕获鼠标输入只有一个简单的技巧.在TPanel此面板上按下鼠标左键(LMB)后,鼠标就会输出.

在那之后,您的代码Panel1.Cursor := crSizeAll;开始运行.正如您在VCL的源代码中所看到的,TControlCM_CURSORCHANGED自身发送消息以设置所需的游标类型.由于TWinControl是基于TControl,它处理此消息并检查是否已捕获鼠标输入.如果不是这样,则TWinControlWM_SETCURSOR自身发送消息以设置新光标.

但微软明确表示该消息WM_SETCURSOR有一点限制:

如果鼠标导致光标在窗口内移动并且未捕获鼠标输入,则发送到窗口.

因此,如果捕获了鼠标输入,则无法更改光标的类型.你可以做的一件事是打电话ReleaseCapture:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  ReleaseCapture;
  Panel1.Cursor := crSizeAll;
end;
Run Code Online (Sandbox Code Playgroud)

但我不建议这样做,因为它打破了鼠标输入的范例(在我看来,一旦按下LMB,它应该保持按下直到它被释放的重要原因(点击按钮),但你是自由的决定如何自己使用ReleaseCapture).

重要!
用户Zig执行的测试证明使用ReleaseCapture方法是不合适的,因为OnMouseUp如果鼠标光标TPanel移出LMB并且LMB已经释放,它禁止生成事件.

有用的信息:

  1. WM_SETCURSOR消息
  2. SetCapture函数
  3. GetCapture功能
  4. ReleaseCapture功能

  • 使用`ReleaseCapture`会产生副作用:点击面板,移动到面板外面,释放按钮,面板保持"crSizeAll"光标. (3认同)
  • @Dima,我的坏.你的代码工作了.在这种情况下,我只是不确定"ReleaseCapture"是否"健康".让我来检查一下...... (2认同)

Rem*_*eau 7

迪马解释了为什么改变Panel1.Cursor没有你想要的效果.

作为替代方案,您可以Screen.Cursor改为:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Screen.Cursor := crSizeAll;
end;

procedure TForm1.Panel1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  Screen.Cursor := crDefault;
end;
Run Code Online (Sandbox Code Playgroud)


Ser*_*yuz 7

由于Dima 解释Cursor面板设置没有效果的原因,面板无法处理WM_SETCURSOR消息,因为只要按下鼠标按钮就捕获鼠标.

要立即设置光标,您可以使用SetCursorAPI.可能的缺点是光标将保持您设置的状态,直到您释放鼠标按钮,甚至在面板外.

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SetCursor(Screen.Cursors[crSizeAll]);
end;
Run Code Online (Sandbox Code Playgroud)

您不需要重置光标,因为在WM_SETCURSOR释放鼠标按钮后将假定默认值(响应消息).


如果您希望光标恢复到面板外部并在再次进入面板时再次设置,则可以包含OnMouseMove事件处理程序.那是因为鼠标消息将被引导到面板,即使在面板外面,因为它捕获了鼠标:

procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if Mouse.Capture = Panel1.Handle then begin
    if PtInRect(Rect(0, 0, Panel1.Width, Panel1.Height), Point(X, Y)) then
      SetCursor(Screen.Cursors[crSizeAll])
    else
      SetCursor(Screen.Cursors[crDefault])
  end;
end;
Run Code Online (Sandbox Code Playgroud)

  • @SertacAkyuz,你是对的.我假设`SetCursor`将与`Screen.Cursor`具有相同的效果,但您描述的差异是有效的.你的解决方案实际上是完美的,但就像我在评论中提到的那样,@ Dima提供了关于原因的完美解释,我根本不能接受不止一个答案:/我真的希望你不介意我接受他的回答.非常感谢队友! (2认同)