游标。当前与此游标
.Net中的Cursor.Current
和this.Cursor
( this
是一个WinForm)有没有区别? 我一直使用this.Cursor
并有很好的运气,但我最近开始使用CodeRush,并在“等待光标”块中embedded一些代码CodeRush使用Cursor.Current
属性。 我在互联网上看到,在工作中,其他程序员在Cursor.Current
属性上遇到了一些问题。 这让我想知道两者是否有所不同。 提前致谢。
我做了一个小testing。 我有两个winforms。 我单击form1上的一个button,将Cursor.Current
属性设置为Cursors.WaitCursor
,然后显示form2。 游标在任何一种forms上都不会改变。 它仍然是Cursors.Default
(指针)游标。
如果我把this.Cursor
设置为Cursors.WaitCursor
在form1上的button点击事件并且显示form2,那么等待游标只在form1上显示,默认光标在form2上。 所以,我仍然不知道Cursor.Current
是什么。
Windows将包含鼠标光标的窗口发送WM_SETCURSOR消息,给它一个改变光标形状的机会。 像TextBox这样的控件利用了这一点,将光标变成了I-bar。 Control.Cursor属性确定将使用的形状。
Cursor.Current属性直接更改形状,而不等待WM_SETCURSOR响应。 在大多数情况下,这种形状不太可能长存。 只要用户移动鼠标,WM_SETCURSOR将其更改回Control.Cursor。
在.NET 2.0中添加了UseWaitCursor属性,以便更容易地显示沙漏。 不幸的是,它不能很好地工作。 它需要一个WM_SETCURSOR消息来改变形状,当你设置属性为真,然后做一些需要一段时间的事情不会发生。 试试这个代码例如:
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); } }
我相信Cursor.Current是当前正在使用的鼠标光标(不pipe它在屏幕上的什么地方),而这个.Cursor是鼠标经过窗口时的光标。
实际上,如果你想从另一个线程中使用HourGlass,它会让你回到跨线程exception,因为你试图从不同的线程访问f.Handle而不是最初创build的表单。 使用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); } }
this.Cursor
是当鼠标移动到这个窗口的时候使用的游标。 Cursor.Current
是当前的鼠标光标,可能与此不同。 this.Cursor
如果鼠标在另一个窗口上。
我注意到了设置游标的一个有趣的事情,所以我想澄清一下我以前有过的一些误解,希望它也能帮助别人:
当您尝试通过使用设置窗体的光标
this.cursor = Cursors.Waitcursor
您实际上将光标设置为控件,而不是整个窗体,因为游标是Control类的属性。
当然,当鼠标实际上超过了实际的控件(显式地是窗体的区域)时,光标只会被改变为给定的光标,
正如Hans Passant所说:
Windows将包含鼠标光标的窗口发送WM_SETCURSOR消息,给它一个改变光标形状的机会
我不知道如果Windows直接发送消息到控件,或者如果窗体中继这些消息到它的子控件基于鼠标位置,我很可能会猜测第一个方法,因为当我提取的重写窗体的WndProc的消息控制,当我在文本框例如,窗体没有处理任何消息。 (请有人澄清这一点)
基本上我的build议是将驻留使用this.cursor还坚持this.usewaitcursor,因为这将游标属性更改为所有子控件的waitcursor。
这个问题也与应用程序级别Application.usewaitcursor相同,当你不在窗体/窗体上时,你的光标没有WM_SETCURSOR消息正在被Windows发送,所以如果你在开始一个耗时的同步操作之前移动你的将鼠标移到窗体的区域上,当耗时的同步操作完成时,窗体只能处理这样的消息。
(我不会build议在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 }
或者你可以像这样在表单中初始化一个局部variables:
public readonly HourGlass hourglass; public Form1() { InitializeComponent(); hourglass = new HourGlass(this, false); }
并在后面的try try catch块中使用它
当LongRunningOperation()正在处理消息时,这对我很有用。
private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e) { this.Cursor = Cursors.WaitCursor; LongRunningOperation(); this.Cursor = Cursors.Arrow; }
从VB.net VS 2012
Windows.Forms.Cursor.Current = Cursors.Default
- 如果.NET中的MemoryStream未closures,是否会产生内存泄漏?
- 为什么WinRT不受pipe理?
- 什么时候应该使用Tracing vs Logger.NET,Enterprise Library,log4net或Ukadc.Diagnostics?
- Decimal.One,Decimal.Zero,Decimal.MinusOne在.Net中的用途是什么?
- 循环引用导致内存泄漏?
- System.Windows.Forms.TextBox中的水印
- 与lambdas一起使用的弱事件处理程序模型
- Web服务 – WCF与ASMX(“标准”)
- 如何实现StringBuilder类? 每次我们追加它是否在内部创build新的string对象?