Cursor.Current vs. this.Cursor

Existe uma diferença entre Cursor.Current e Cursor.Current (onde this é um WinForm) em .net? Eu sempre usei this.Cursor e tive muita sorte com isso, mas eu recentemente comecei a usar CodeRush e apenas embuti algum código em um bloco “Wait Cursor” e CodeRush usou a propriedade Cursor.Current . Eu vi na Internet e no trabalho onde outros programadores tiveram alguns problemas com a propriedade Cursor.Current . Acabei de me perguntar se há uma diferença nos dois. Desde já, obrigado.

Eu fiz um pequeno teste. Eu tenho duas winforms. Eu clico em um botão no form1, defina a propriedade Cursors.WaitCursor como Cursors.WaitCursor e, em seguida, mostre form2. O cursor não muda em nenhum dos formulários. Permanece cursor Cursors.Default (ponteiro).

Se eu definir Cursors.WaitCursor para Cursors.WaitCursor no evento de clique do botão no form1 e show form2, o cursor de espera só mostra no form1 eo cursor padrão está no form2 que é esperado. Então, eu ainda não sei o que o Cursor.Current faz.

O Windows envia a janela que contém o cursor do mouse a mensagem WM_SETCURSOR, dando a oportunidade de alterar a forma do cursor. Um controle como o TextBox aproveita isso, mudando o cursor para um I-bar. A propriedade Control.Cursor determina qual forma será usada.

A propriedade Cursor.Current altera a forma diretamente, sem esperar por uma resposta WM_SETCURSOR. Na maioria dos casos, é improvável que essa forma sobreviva por muito tempo. Assim que o usuário move o mouse, o WM_SETCURSOR o altera para Control.Cursor.

A propriedade UseWaitCursor foi adicionada no .NET 2.0 para facilitar a exibição de uma ampulheta. Infelizmente, isso não funciona muito bem. Ele requer uma mensagem WM_SETCURSOR para alterar a forma e isso não acontecerá quando você definir a propriedade como true e, em seguida, fizer algo que demore um pouco. Tente este código por exemplo:

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

O cursor nunca muda. Para fazer isso, você precisará usar o Cursor.Current também. Aqui está uma pequena aula auxiliar para facilitar:

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

E usá-lo assim:

 private void button1_Click(object sender, EventArgs e) { using (new HourGlass()) { System.Threading.Thread.Sleep(3000); } } 

Eu acredito que Cursor.Current é o cursor do mouse atualmente sendo usado (independentemente de onde ele está na canvas), enquanto this.Cursor é o cursor para o qual ele será configurado, quando o mouse passar sobre sua janela.

Na verdade, se você gostaria de usar HourGlass de outro segmento que lhe dará de volta exceção cross-threading porque você está tentando acessar f.Handle de thread diferente do formulário foi originalmente criado. Use GetForegroundWindow () em vez de user32.dll.

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

e depois

 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 é o cursor que será usado quando o mouse estiver sobre a janela referida por this . Cursor.Current é o cursor do mouse atual, que pode ser diferente this.Cursor se o mouse estiver sobre uma janela diferente.

Tenho notado uma coisa interessante sobre a configuração de cursores, então eu gostaria de esclarecer alguns mal-entendidos que eu mesmo tive antes e espero que isso ajude os outros também:

Quando você tenta definir o cursor de um formulário usando

this.cursor = Cursors.Waitcursor

Na verdade, você define o cursor para o controle e não o formulário inteiro, já que o cursor é propriedade da class Control.

Também é claro que o cursor só será alterado para o cursor dado quando o mouse estiver realmente sobre o controle real (explicitamente a área do formulário)

Como Hans Passant já declarou que:

Windows envia a janela que contém o cursor do mouse a mensagem WM_SETCURSOR, dando-lhe uma oportunidade para alterar a forma do cursor

Eu não sei se o Windows envia mensagens diretamente para controles ou se o formulário retransmite essas mensagens para seus controles filho com base na posição do mouse, eu provavelmente acho que no primeiro método desde quando eu busquei as mensagens com substituindo WndProc do formulário controle, quando eu estava sobre o textbox por exemplo, o formulário não processou nenhuma mensagem. (por favor, alguém dê clareza sobre isso)

Basicamente, minha sugestão seria residir usando this.cursor também e ficar com this.usewaitcursor, já que isso muda a propriedade do cursor para waitcursor para todos os controles filhos.

O problema com isso também é o mesmo que com o nível de aplicativo Application.usewaitcursor, enquanto você não está sobre o formulário / formulários com seu cursor nenhuma mensagem WM_SETCURSOR está sendo enviada pelo windows, portanto, se você iniciar uma operação síncrona demorada antes de mover seu mouse sobre a área do formulário, o formulário só pode processar essa mensagem quando a operação síncrona demorada terminar.

(Eu não sugeriria executar tarefas demoradas no thread da interface do usuário, principalmente é isso que está causando o problema aqui)

Fiz uma pequena melhora na resposta de Hans Passant, portanto, a ampulheta pode ser definida no nível do aplicativo ou no nível do formulário, evitando também InvalidOperationException das chamadas de operação de thread cruzado:

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

Para usá-lo no nível do aplicativo:

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

Para usá-lo no nível de formulário, você pode usar o formulário ativo atual:

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

ou você pode inicializar uma variável local no formulário da seguinte forma:

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

e usá-lo mais tarde em uma tentativa de capturar finalmente bloquear

Isso funciona muito bem para mim quando o LongRunningOperation () está processando mensagens.

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

Do VB.net VS 2012

 Windows.Forms.Cursor.Current = Cursors.Default