57

Cursor.Current.Net中和this.CursorthisWinForm在哪里)之间有区别吗?我一直使用this.Cursor它并且运气很好,但我最近开始使用 CodeRush 并且只是在“等待光标”块中嵌入了一些代码,并且 CodeRush 使用了该Cursor.Current属性。我在 Internet 和工作中看到其他程序员对该Cursor.Current属性有一些问题。这让我想知道两者是否有区别。提前致谢。

我做了一个小测试。我有两个winforms。我单击 form1 上的一个按钮,将Cursor.Current属性设置为Cursors.WaitCursor然后显示 form2。光标在任何一种形式上都不会改变。它仍然是Cursors.Default(指针)光标。

如果我在 form1 上的按钮单击事件中设置this.CursorCursors.WaitCursor并显示 form2,则等待光标仅显示在 form1 上,默认光标在 form2 上,这是预期的。所以,我仍然不知道是什么Cursor.Current

4

7 回答 7

90

Windows 向包含鼠标光标的窗口发送 WM_SETCURSOR 消息,使其有机会更改光标形状。像 TextBox 这样的控件利用了这一点,将光标更改为 I-bar。Control.Cursor 属性确定将使用什么形状。

Cursor.Current 属性直接更改形状,无需等待 WM_SETCURSOR 响应。在大多数情况下,这种形状不太可能长期存在。只要用户移动鼠标,WM_SETCURSOR 就会将它改回 Control.Cursor。

.NET 2.0 中添加了 UseWaitCursor 属性,以便更轻松地显示沙漏。不幸的是,它的效果不是很好。它需要一条 WM_SETCURSOR 消息来更改形状,并且当您将属性设置为 true 然后执行需要一段时间的操作时,不会发生这种情况。试试这个代码,例如:

private void button1_Click(object sender, EventArgs e) {
  this.UseWaitCursor = true;
  System.Threading.Thread.Sleep(3000);
  this.UseWaitCursor = false;
}

光标永远不会改变。要将其打造成形状,您还需要使用 Cursor.Current。这是一个小助手类,可以使它变得容易:

using System;
using System.Windows.Forms;

public class HourGlass : IDisposable {
  public HourGlass() {
    Enabled = true;
  }
  public void Dispose() {
    Enabled = false;
  }
  public static bool Enabled {
    get { return Application.UseWaitCursor; }
    set {
      if (value == Application.UseWaitCursor) return;
      Application.UseWaitCursor = value;
      Form f = Form.ActiveForm;
      if (f != null && f.Handle != IntPtr.Zero)   // Send WM_SETCURSOR
        SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1);
    }
  }
  [System.Runtime.InteropServices.DllImport("user32.dll")]
  private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

并像这样使用它:

private void button1_Click(object sender, EventArgs e) {
  using (new HourGlass()) {
    System.Threading.Thread.Sleep(3000);
  }
}
于 2008-11-19T18:23:19.190 回答
11

我相信 Cursor.Current 是当前正在使用的鼠标光标(无论它在屏幕上的哪个位置),而 this.Cursor 是当鼠标经过您的窗口时它将被设置的光标。

于 2008-11-19T17:19:32.750 回答
6

this.Cursor是当鼠标悬停在 引用的窗口上时将使用的光标this。 是当前鼠标光标,如果鼠标在不同的窗口上Cursor.Current,它可能会有所不同。this.Cursor

于 2008-11-19T17:20:35.070 回答
6

实际上,如果您想从另一个线程使用 HourGlass,这会给您返回跨线程异常,因为您尝试从与最初创建的表单不同的线程访问 f.Handle。使用来自 user32.dll 的 GetForegroundWindow()。

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

进而

public static bool Enabled
{
    get
    {
        return Application.UseWaitCursor;
    }

    set
    {
        if (value == Application.UseWaitCursor)
        {
            return;
        }

        Application.UseWaitCursor = value;
        var handle = GetForegroundWindow();
        SendMessage(handle, 0x20, handle, (IntPtr)1);
    }
}
于 2011-12-15T05:45:26.133 回答
3

我注意到关于设置光标的一个有趣的事情,所以我想澄清一下我自己之前的一些误解,希望它也可以帮助其他人:

当您尝试使用设置表单的光标时

this.cursor = 光标.Waitcursor

您实际上为控件设置了光标,而不是整个表单,因为光标是 Control 类的属性。

当然,只有当鼠标实际位于实际控件上时光标才会更改为给定的光标(明确地是表单的区域)

正如 Hans Passant 已经说过的那样:

Windows 向包含鼠标光标的窗口发送 WM_SETCURSOR 消息,使其有机会更改光标形状

我不知道 Windows 是否直接向控件发送消息,或者表单是否根据鼠标位置将这些消息中继到它的子控件,我很可能会猜测第一种方法,因为当我使用表单的覆盖 WndProc 获取消息时控件,例如,当我在文本框上时,表单没有处理任何消息。(请有人澄清这一点)

基本上我的建议是停止使用 this.cursor 并坚持使用 this.usewaitcursor,因为这会将所有子控件的 cursor 属性更改为 waitcursor。

这个问题也与应用程序级别 Application.usewaitcursor 相同,当您没有使用光标在表单/表单上时,Windows 不会发送 WM_SETCURSOR 消息,因此如果您在移动之前启动耗时的同步操作将鼠标悬停在表单区域上,只有在耗时的同步操作完成后,表单才能处理此类消息。

(我根本不建议在 UI 线程中运行耗时的任务,主要是这导致了这里的问题)

我对 Hans Passant 的回答做了一点改进,所以沙漏可以设置在应用程序级别或表单级别,也可以避免来自跨线程操作调用的 InvalidOperationException:

using System;
using System.Windows.Forms;

public class HourGlass : IDisposable
{
    public static bool ApplicationEnabled
    {
        get{ return Application.UseWaitCursor; }
        set
        {
            Form activeFrom = Form.ActiveForm;
            if (activeFrom == null || ApplicationEnabled == value) return;
            if (ApplicationEnabled == value)return;
            Application.UseWaitCursor = (bool)value;

            if (activeFrom.InvokeRequired)
            {
                activeFrom.BeginInvoke(new Action(() =>
                {
                    if (activeFrom.Handle != IntPtr.Zero)
                    SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
                }));
            }
            else
            {
                if (activeFrom.Handle != IntPtr.Zero)
                SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
            }
        }
    }

    private Form f;

    public HourGlass() 
    {
        this.f = Form.ActiveForm;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = true;
    }

    public HourGlass(bool enabled)
    {
        this.f = Form.ActiveForm;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = enabled;
    }

    public HourGlass(Form f, bool enabled)
    {
        this.f = f;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = enabled;
    }

    public HourGlass(Form f)
    {
        this.f = f;

        if (f == null)
        {
            throw new ArgumentException();
        }

        Enabled = true;
    }

    public void Dispose()
    {
        Enabled = false;
    }

    public bool Enabled
    {
        get { return f.UseWaitCursor; }
        set
        {
            if (f == null || Enabled == value) return;
            if (Application.UseWaitCursor == true && value == false) return;

            f.UseWaitCursor = (bool)value;

            if(f.InvokeRequired)
            {
                f.BeginInvoke(new Action(()=>
                {
                    if (f.Handle != IntPtr.Zero)
                    SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
                }));
            }
            else
            {
                if (f.Handle != IntPtr.Zero)
                SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
            }
        }
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

要在应用程序级别使用它:

try
{
  HourGlass.ApplicationEnabled = true;
  //time consuming synchronous task
}
finally
{
  HourGlass.ApplicationEnabled = false;
}

要在表单级别使用它,您可以将其用于当前活动表单:

using (new HourGlass())
{
  //time consuming synchronous task
}

或者你可以像这样初始化一个局部变量:

public readonly HourGlass hourglass;

public Form1()
{
    InitializeComponent();
    hourglass = new HourGlass(this, false);
}

稍后在 try catch finally 块中使用它

于 2016-04-30T20:29:06.413 回答
0

当 LongRunningOperation() 处理消息时,这对我很有用。

private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    LongRunningOperation();
    this.Cursor = Cursors.Arrow;
}
于 2015-02-15T03:40:49.723 回答
0

来自 VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default
于 2015-03-12T19:44:00.853 回答