cursor
Posted shanyu20
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cursor相关的知识,希望对你有一定的参考价值。
Cursor.Current 与 this.Cursor
【中文标题】Cursor.Current 与 this.Cursor【英文标题】:Cursor.Current vs. this.Cursor 【发布时间】:2010-09-23 02:13:09 【问题描述】:.Net 中的Cursor.Current
和this.Cursor
(其中this
是WinForm)之间有区别吗?我一直使用this.Cursor
并且运气很好,但我最近开始使用 CodeRush 并且只是在“等待光标”块中嵌入了一些代码,CodeRush 使用了Cursor.Current
属性。我在 Internet 和工作中看到其他程序员在使用 Cursor.Current
属性时遇到了一些问题。这让我想知道两者是否有区别。提前致谢。
我做了一个小测试。我有两个winforms。我单击 form1 上的按钮,将 Cursor.Current
属性设置为 Cursors.WaitCursor
,然后显示 form2。光标在任何一种形式上都不会改变。它仍然是Cursors.Default
(指针)光标。
如果我在 form1 上的按钮单击事件中将 this.Cursor
设置为 Cursors.WaitCursor
并显示 form2,则等待光标仅显示在 form1 上,默认光标在 form2 上,这是预期的。所以,我还是不知道Cursor.Current
做了什么。
【问题讨论】:
【参考方案1】:我相信 Cursor.Current 是当前正在使用的鼠标光标(不管它在屏幕上的什么位置),而 this.Cursor 是当鼠标经过您的窗口时它将被设置的光标。
【讨论】:
这似乎不正确。我创建了一个示例应用程序来验证这一点,似乎System.Windows.Cursors.Current
仅在光标更改与应用程序窗口相关联时更新。
不同之处在于this.Cursor
不会更新,即使光标位于窗口的子控件或窗口的非客户区上。很抱歉后续的两个 cmet,编辑第一个的时间已经结束。【参考方案2】:
this.Cursor
是当鼠标悬停在this
引用的窗口上时将使用的光标。 Cursor.Current
是当前鼠标光标,如果鼠标在不同的窗口上,它可能与 this.Cursor
不同。
【讨论】:
【参考方案3】: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);
【讨论】:
我在将其与启动屏幕结合使用时遇到了一个案例,这会导致 InvalidOperationException - “跨线程操作无效”。在 f!=null 和 f.Handle!=null 之间添加一个 !f.InvokeRequired 解决了这个问题。 这对我很有用,但根据 ReSharper,这一行的“表达式始终为真”: if (f != null && f.Handle != null) // 发送 WM_SETCURSOR 这是一个很棒的助手类。在没有其他事情的情况下工作。 如果在慢代码运行结束后 无法让光标更新,那么你做错了。 你可能想把上面的代码改成这样:` Form f = Form.ActiveForm; f.Invoke(() => if (f != null && f.Handle != IntPtr.Zero) // 发送 WM_SETCURSOR SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); ); ` 此更改让您可以从后台线程调用 HourGlass.Enabled(避免跨线程调用和相应的异常)。萨沙【参考方案4】:实际上,如果您想从另一个线程使用 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);
【讨论】:
对不起!我遇到了运行时错误,但这是我的错!我错过了方法签名顶部的[DllImport("user32.dll")]
。效果很好!
对于我的跨线程情况,这是唯一可靠的解决方案。【参考方案5】:
当 LongRunningOperation() 处理消息时,这对我很有用。
private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
this.Cursor = Cursors.WaitCursor;
LongRunningOperation();
this.Cursor = Cursors.Arrow;
【讨论】:
如果 LongRunningOperation 失败了怎么办?至少应该在这里尝试/最后。另外,如果光标不是以例如箭头开头的箭头怎么办?你在文本框里吗?【参考方案6】:来自 VB.net VS 2012
Windows.Forms.Cursor.Current = Cursors.Default
【讨论】:
我不确定这如何回答主要问题。你能澄清一下吗? 这只是从 Cursor 类而不是 Cursor 属性访问当前光标的方法。【参考方案7】:我注意到关于设置光标的一个有趣的事情,所以我想澄清一下我自己之前的一些误解,希望它也可以帮助别人:
当您尝试使用设置表单的光标时
this.cursor = Cursors.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 块中使用它
【讨论】:
以上是关于cursor的主要内容,如果未能解决你的问题,请参考以下文章
PLSQL中显示Cursor隐示Cursor动态Ref Cursor差别
python 中的 cursor.fetchall() 与 list(cursor)