Cursor.Current与this.Cursor

时间:2008-11-19 17:17:49

标签: c# .net winforms cursor cursor.current

.Net中Cursor.Currentthis.Cursor(其中this是WinForm)之间是否存在差异?我总是使用this.Cursor并且运气很好但我最近开始使用CodeRush并且只是在“Wait Cursor”块中嵌入了一些代码而CodeRush使用了Cursor.Current属性。我已经在互联网和工作中看到其他程序员遇到Cursor.Current属性的问题。它让我想知道这两者是否存在差异。提前谢谢。

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

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

7 个答案:

答案 0 :(得分:87)

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);
  }
}

答案 1 :(得分:10)

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

答案 2 :(得分:6)

实际上,如果你想从另一个线程中使用HourGlass,它会给你回交叉线程异常,因为你试图从不同的线程访问f.Handle而不是最初创建的形式。使用GetForegroundWindow()代替user32.dll。

[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);
    }
}

答案 3 :(得分:5)

this.Cursor是当鼠标位于this引用的窗口上时将使用的光标。 Cursor.Current是当前鼠标光标,如果鼠标位于不同窗口,则可能与this.Cursor不同。

答案 4 :(得分:2)

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

尝试使用

设置表单的光标时

this.cursor = Cursors.Waitcursor

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

当然,当鼠标实际位于实际控件上时(显式位于表单区域),光标只会更改为给定的光标

正如Hans Passant已经说过:

  

Windows发送包含鼠标光标的窗口   WM_SETCURSOR消息,给它一个改变光标的机会   形状

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

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

这个问题也与应用程序级别的Application.usewaitcursor相同,当你没有光标的窗体/窗体上没有窗口发送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块中使用它

答案 5 :(得分:0)

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

private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    LongRunningOperation();
    this.Cursor = Cursors.Arrow;
}

答案 6 :(得分:0)

来自VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default