Domanda

C'è una differenza tra Cursor.Current e this.Cursor (dove this è un WinForm) in .Net? Ho sempre usato this.Cursor e ho avuto molta fortuna con esso, ma di recente ho iniziato a usare CodeRush e ho appena incorporato del codice in un " Wait Cursor " block e CodeRush hanno usato la proprietà Cursor.Current . Ho visto su Internet e al lavoro in cui altri programmatori hanno avuto dei problemi con la proprietà Cursor.Current . Mi viene solo da chiedermi se c'è una differenza tra i due. Grazie in anticipo.

Ho fatto un piccolo test. Ho due winform. Faccio clic su un pulsante in form1, imposto la proprietà Cursor.Current su Cursors.WaitCursor e quindi visualizzo form2. Il cursore non cambia su nessuno dei due moduli. Rimane il cursore Cursors.Default (puntatore).

Se imposto this.Cursor su Cursors.WaitCursor nell'evento clic sul pulsante su form1 e mostra form2, il cursore di attesa viene visualizzato solo su form1 e il cursore predefinito è su form2 che è previsto. Quindi, non so ancora cosa faccia Cursor.Current .

È stato utile?

Soluzione

Windows invia alla finestra che contiene il cursore del mouse il messaggio WM_SETCURSOR, dandogli l'opportunità di cambiare la forma del cursore. Un controllo come TextBox ne approfitta, trasformando il cursore in una I-bar. La proprietà Control.Cursor determina quale forma verrà utilizzata.

La proprietà Cursor.Current cambia direttamente la forma, senza attendere una risposta WM_SETCURSOR. Nella maggior parte dei casi, è improbabile che quella forma sopravviva a lungo. Non appena l'utente sposta il mouse, WM_SETCURSOR lo cambia di nuovo in Control.Cursor.

La proprietà UseWaitCursor è stata aggiunta in .NET 2.0 per facilitare la visualizzazione di una clessidra. Sfortunatamente, non funziona molto bene. Richiede un messaggio WM_SETCURSOR per cambiare la forma e ciò non accadrà quando imposti la proprietà su true e fai qualcosa che richiede del tempo. Prova ad esempio questo codice:

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

Il cursore non cambia mai. Per dargli una forma, dovrai usare anche Cursor.Current. Ecco una piccola classe di supporto per semplificare:

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 usalo in questo modo:

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

Altri suggerimenti

Credo che Cursor.Current sia il cursore del mouse attualmente in uso (indipendentemente da dove si trova sullo schermo), mentre this.Cursor è il cursore su cui verrà impostato quando il mouse passa sopra la finestra.

this.Cursor è il cursore che verrà utilizzato quando il mouse si trova sulla finestra indicata da this . Cursor.Current è il cursore del mouse corrente, che potrebbe essere diverso da this.Cursor se il mouse si trova su una finestra diversa.

In realtà se desideri utilizzare HourGlass da un altro thread che ti restituirà un'eccezione cross-thread perché stai provando ad accedere a f.Handle da un thread diverso da quello originariamente creato. Utilizzare GetForegroundWindow () invece da user32.dll.

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

e poi

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

Ho notato una cosa interessante sull'impostazione dei cursori, quindi vorrei chiarire alcuni fraintendimenti che avevo prima e spero che possa aiutare anche gli altri:

Quando si tenta di impostare il cursore di un modulo utilizzando

this.cursor = Cursors.Waitcursor

in realtà si imposta il cursore per il controllo e non l'intero modulo poiché il cursore è di proprietà della classe Control.

Inoltre, ovviamente, il cursore verrà cambiato nel cursore dato solo quando il mouse si trova effettivamente sopra il controllo effettivo (esplicitamente l'area del modulo)

Come ha già affermato Hans Passant:

  

Windows invia la finestra che contiene il cursore del mouse il   Messaggio WM_SETCURSOR, che offre l'opportunità di cambiare il cursore   forma

Non so se Windows invia messaggi direttamente ai controlli o se il modulo inoltra tali messaggi ai suoi controlli figlio in base alla posizione del mouse, molto probabilmente indovinerei il primo metodo da quando ho recuperato i messaggi con WndProc ignorante del controllo del modulo, ad esempio quando ero sopra la casella di testo, il modulo non elaborava alcun messaggio. (per favore qualcuno dia chiarezza su questo)

Fondamentalmente il mio suggerimento sarebbe di risiedere nell'usare anche this.cursor e attenersi a this.usewaitcursor, poiché ciò modifica la proprietà del cursore in waitcursor per tutti i controlli figlio.

Il problema con questo è anche lo stesso dell'applicazione Application.usewaitcursor a livello di applicazione, mentre non ci si trova sopra il modulo / i moduli con il cursore, nessun messaggio WM_SETCURSOR viene inviato da Windows, quindi se si avvia un'operazione sincrona che richiede tempo prima di spostare il mouse sull'area del modulo, il modulo può elaborare tale messaggio solo al termine dell'operazione sincrona che richiede tempo.

(Non consiglierei affatto di eseguire attività che richiedono tempo nel thread dell'interfaccia utente, principalmente questo è ciò che sta causando il problema qui)

Ho apportato un piccolo miglioramento alla risposta di Hans Passant, quindi la clessidra può essere impostata sia a livello di applicazione che a livello di modulo, evitando anche InvalidOperationException da chiamate di operazioni incrociate:

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

Per usarlo a livello di applicazione:

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

Per utilizzarlo a livello di modulo puoi utilizzare il modulo attivo corrente:

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

oppure puoi inizializzare una variabile locale nel modulo in questo modo:

public readonly HourGlass hourglass;

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

e utilizzarlo in seguito in un blocco try catch finally

Per me funziona benissimo quando LongRunningOperation () elabora i messaggi.

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

Da VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top