Cursor.Current 与 this.Cursor

发布于 2024-07-08 11:27:46 字数 698 浏览 11 评论 0原文

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

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

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

Is there a difference between Cursor.Current and this.Cursor (where this is a WinForm) in .Net? I've always used this.Cursor and have had very good luck with it but I've recently started using CodeRush and just embedded some code in a "Wait Cursor" block and CodeRush used the Cursor.Current property. I've seen on the Internet and at work where other programmers have had some problems with the Cursor.Current property. It just got me to wondering if there is a difference in the two. Thanks in advance.

I did a little test. I have two winforms. I click a button on form1, set the Cursor.Current property to Cursors.WaitCursor and then show form2. The cursor doesn't change on either form. It remains Cursors.Default (pointer) cursor.

If I set this.Cursor to Cursors.WaitCursor in the button click event on form1 and show form2, the wait cursor only shows on form1 and the default cursor is on form2 which is expected. So, I still don't know what Cursor.Current does.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

凡尘雨 2024-07-15 11:27:46

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

Windows sends the window that contains the mouse cursor the WM_SETCURSOR message, giving it an opportunity to change the cursor shape. A control like TextBox takes advantage of that, changing the cursor into a I-bar. The Control.Cursor property determines what shape will be used.

The Cursor.Current property changes the shape directly, without waiting for a WM_SETCURSOR response. In most cases, that shape is unlikely to survive for long. As soon as the user moves the mouse, WM_SETCURSOR changes it back to Control.Cursor.

The UseWaitCursor property was added in .NET 2.0 to make it easier to display an hourglass. Unfortunately, it doesn't work very well. It requires a WM_SETCURSOR message to change the shape and that won't happen when you set the property to true and then do something that takes a while. Try this code for example:

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

The cursor never changes. To whack that into shape, you'll need to use Cursor.Current as well. Here is a little helper class to make it easy:

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

And use it like this:

private void button1_Click(object sender, EventArgs e) {
  using (new HourGlass()) {
    System.Threading.Thread.Sleep(3000);
  }
}
小耗子 2024-07-15 11:27:46

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

I believe that Cursor.Current is the mouse cursor currently being used (regardless of where it is on the screen), while this.Cursor is the cursor it will be set to, when the mouse passes over your window.

一人独醉 2024-07-15 11:27:46

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

this.Cursor is the cursor that will be used when the mouse is over the window referred to by this. Cursor.Current is the current mouse cursor, which might be different from this.Cursor if the mouse is over a different window.

谁对谁错谁最难过 2024-07-15 11:27:46

实际上,如果您想从另一个线程使用 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);
    }
}

Actually if you would like to use HourGlass from another thread that will give you back cross-threading exception because you are trying to access f.Handle from different thread than form was originally created. Use GetForegroundWindow() instead from user32.dll.

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

and then

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);
    }
}
三人与歌 2024-07-15 11:27:46

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

设置表单的光标时,

当您尝试使用this.cursor = Cursors.Waitcursor

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

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

正如 Hans Passant 已经指出的那样:

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

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

基本上我的建议是不要使用 this.cursor 并坚持使用 this.usewaitcursor,因为这会将所有子控件的光标属性更改为 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 块

I have noticed an interesting thing about setting cursors, so I would like to clear some misunderstandings that I myself had before and I hope it may help others too:

When you try to set a form's cursor by using

this.cursor = Cursors.Waitcursor

you actually set the cursor for the control and not the whole form since cursor is property of the Control class.

Also of course the cursor will only be changed to the given cursor when the mouse is actually over the actual control (explicitly the form's area)

As Hans Passant has already stated that:

Windows sends the window that contains the mouse cursor the
WM_SETCURSOR message, giving it an opportunity to change the cursor
shape

I don't know if windows sends messages directly to controls or if the form relays those messages to it's child controls based on mouse position, I'd most likely guess on the first method since when i fetched the messages with overriding WndProc of the form control, when i was over the textbox for example, the form didn't process any messages. (please someone give clarity on this)

Basically my suggestion would be to reside from using this.cursor also and stick to this.usewaitcursor, since that changes the cursor property to waitcursor for all child controls.

The problem with this is also the same as with the application level Application.usewaitcursor, while you are not over the form/forms with your cursor no WM_SETCURSOR message is being sent by windows, so if you start a time consuming synchronous operation before moving your mouse over the form's area, the form can only process such message when the time consuming synchronous operation finishes.

(I would not suggest running time consuming tasks in the UI thread at all, mainly this is what is causing the issue here)

I made a little improvement on Hans Passant's answer, so the hourglass can be either set on application level or form level, also avoiding InvalidOperationException from cross threaded operation calls:

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

To use it on application level:

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

For using it on form level you can either use for the current active form:

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

or you can initialize a local variable in the form like this:

public readonly HourGlass hourglass;

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

and use it later in a try catch finally block

删除会话 2024-07-15 11:27:46

当 LongRunningOperation() 处理消息时,这对我来说非常有用。

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

This works great for me when the LongRunningOperation() is processing messages.

private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    LongRunningOperation();
    this.Cursor = Cursors.Arrow;
}
智商已欠费 2024-07-15 11:27:46

来自 VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default

From VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default
一曲爱恨情仇 2024-07-15 11:27:46

Hans Passant 的代码对我来说没有用,因为我经常在一个类中设置等待光标并在另一个类中重置它。

例如,用户单击“连接”按钮,显示等待光标,在后台线程中建立连接,并且当准备就绪时,必须将光标重置为正常。

Hans 的 Hourglass 类对于这种情况来说太不灵活了。
此外,设置光标也不需要 Windows 消息。

我在 Utils 类中使用它:

public class Utils
{
    static Form mi_WaitCursorForm;

    // i_Control may be a Control or a Form
    public static void ShowWaitCursor(Control i_Control)
    {
        // Wait cursor may still be set for another Form
        HideWaitCursor();

        Form i_Form = i_Control as Form;
        if (i_Form == null)
        {
            if (i_Control != null)
            {
                i_Form = (Form)i_Control.TopLevelControl;
            }
            else
            {
                Debug.Assert(false, "A control or Form should be specified!");
                i_Form = Form.ActiveForm;
            }
        }

        // i_Form.UseWaitCursor = true does NOT work always
        i_Form.Cursor = Cursors.WaitCursor;
        mi_WaitCursorForm = i_Form;
        Application.DoEvents();
    }

    public static void HideWaitCursor()
    {
        Form i_Form = mi_WaitCursorForm;
        if (i_Form == null)
            return;
            
        i_Form.Cursor = Cursors.Arrow;
        mi_WaitCursorForm = null;
       
        // Fix for bug in .NET framework: sometimes the wait cursor does not disappear.
        Cursor.Position = Cursor.Position;
        Application.DoEvents();
    }
}

The code from Hans Passant is not useful for me because it happens very often that I set the wait cursor in one class and reset it in another class.

For example the user clicks a button "Connect", the wait cursor is shown, in a background thread a connection is established and when it is ready the cursor must be reset to normal.

The Hourglass class from Hans is too unflexible for this scenario.
Also a Windows Message is not required to set the cursor.

I use this in a class Utils:

public class Utils
{
    static Form mi_WaitCursorForm;

    // i_Control may be a Control or a Form
    public static void ShowWaitCursor(Control i_Control)
    {
        // Wait cursor may still be set for another Form
        HideWaitCursor();

        Form i_Form = i_Control as Form;
        if (i_Form == null)
        {
            if (i_Control != null)
            {
                i_Form = (Form)i_Control.TopLevelControl;
            }
            else
            {
                Debug.Assert(false, "A control or Form should be specified!");
                i_Form = Form.ActiveForm;
            }
        }

        // i_Form.UseWaitCursor = true does NOT work always
        i_Form.Cursor = Cursors.WaitCursor;
        mi_WaitCursorForm = i_Form;
        Application.DoEvents();
    }

    public static void HideWaitCursor()
    {
        Form i_Form = mi_WaitCursorForm;
        if (i_Form == null)
            return;
            
        i_Form.Cursor = Cursors.Arrow;
        mi_WaitCursorForm = null;
       
        // Fix for bug in .NET framework: sometimes the wait cursor does not disappear.
        Cursor.Position = Cursor.Position;
        Application.DoEvents();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文