2008-11-20 19 views
103

En varios de mis controles de usuario, cambio el cursor usandoCambiar el cursor en WPF veces funciona, a veces no

this.Cursor = Cursors.Wait; 

cuando hago clic en algo.

Ahora quiero hacer lo mismo en una página de WPF con un clic de botón. Cuando sobrevuelvo mi botón, el cursor cambia a una mano, pero cuando hago clic en él, no cambia al cursor de espera. Me pregunto si esto tiene algo que ver con el hecho de que es un botón, o porque esta es una página y no un control de usuario. Esto parece un comportamiento extraño.

Respuesta

176

¿Necesita que el cursor sea un cursor de "espera" solo cuando haya terminado esa página/control de usuario en particular? Si no es así, me gustaría sugerir el uso de Mouse.OverrideCursor:

Mouse.OverrideCursor = Cursors.Wait; 
try 
{ 
    // do stuff 
} 
finally 
{ 
    Mouse.OverrideCursor = null; 
} 

Esto anula el cursor para su aplicación en lugar de sólo una parte de su interfaz de usuario, por lo que el problema que se está describiendo desaparece.

+0

similar a la mía [respuesta] (http://stackoverflow.com/a/8211178/448232), de fecha 3 años más tarde (casi exactamente!). Me gustan las respuestas en esta pregunta, pero la más simple siempre es la más tentadora :) –

+0

Esta solución cambiará el cursor para que sea un cursor de "espera" pero no deshabilitará ninguna entrada adicional del mouse. Traté de usar esta solución y, aunque el mouse cambió al cursor de espera, todavía puedo hacer clic en cualquier elemento de UI dentro de mi aplicación WPF sin ningún problema. ¿Alguna idea de cómo puedo evitar que el usuario use realmente el mouse durante el cursor de espera está activo? –

+1

Viejo como es y aceptado como es, NO es la respuesta correcta. Anular el cursor de la aplicación es diferente a anular un cursor de control (y el segundo tiene problemas en WPF). Anular el cursor de la aplicación puede tener efectos secundarios desagradables, por ejemplo, un cuadro de mensaje emergente (error) puede verse forzado a usar el mismo cursor reemplazado erróneamente, mientras que la intención era anular solo mientras el mouse está sobre el control real y activo. –

57

Una forma de hacerlo en nuestra aplicación es usar IDisposable y luego con los bloques using(){} para asegurar que el cursor se reinicie cuando termine.

public class OverrideCursor : IDisposable 
{ 

    public OverrideCursor(Cursor changeToCursor) 
    { 
    Mouse.OverrideCursor = changeToCursor; 
    } 

    #region IDisposable Members 

    public void Dispose() 
    { 
    Mouse.OverrideCursor = null; 
    } 

    #endregion 
} 

y luego en el código:

using (OverrideCursor cursor = new OverrideCursor(Cursors.Wait)) 
{ 
    // Do work... 
} 

La anulación terminará cuando: el final de la instrucción using se alcanza o; si se lanza una excepción y el control deja el bloque de declaración antes del final de la instrucción.

actualización

Para evitar que el cursor parpadeante que puede hacer:

public class OverrideCursor : IDisposable 
{ 
    static Stack<Cursor> s_Stack = new Stack<Cursor>(); 

    public OverrideCursor(Cursor changeToCursor) 
    { 
    s_Stack.Push(changeToCursor); 

    if (Mouse.OverrideCursor != changeToCursor) 
     Mouse.OverrideCursor = changeToCursor; 
    } 

    public void Dispose() 
    { 
    s_Stack.Pop(); 

    Cursor cursor = s_Stack.Count > 0 ? s_Stack.Peek() : null; 

    if (cursor != Mouse.OverrideCursor) 
     Mouse.OverrideCursor = cursor; 
    } 

} 
+1

Buen toque con la pila. -john – jschroedl

+0

Gracias John. Es muy útil cuando comienza a anidar, el cursor cambia cuando hay múltiples puntos de entrada. También tenga en cuenta las comprobaciones para evitar que el cursor parpadee. Equivale básicamente al mismo IL que la respuesta seleccionada. – Dennis

+2

Buena solución con la parte que usa. De hecho, escribí exactamente lo mismo en algunos de nuestros proyectos (sin la pila, eso es). Una cosa que puedes simplificar en el uso es simplemente escribir: usando (nuevo OverrideCursor (Cursors.Wait)) {// hacer cosas} en lugar de asignarle una variable que probablemente no uses. – Olli

36

Se puede utilizar un activador de datos (con un modelo de vista) en el botón para activar un cursor de espera.

<Button x:Name="NextButton" 
     Content="Go" 
     Command="{Binding GoCommand }"> 
    <Button.Style> 
     <Style TargetType="{x:Type Button}"> 
      <Setter Property="Cursor" Value="Arrow"/> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Path=IsWorking}" Value="True"> 
        <Setter Property="Cursor" Value="Wait"/> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </Button.Style> 
</Button> 

Este es el código de la vista-modelo:

public class MainViewModel : ViewModelBase 
{ 
    // most code removed for this example 

    public MainViewModel() 
    { 
     GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand); 
    } 

    // flag used by data binding trigger 
    private bool _isWorking = false; 
    public bool IsWorking 
    { 
     get { return _isWorking; } 
     set 
     { 
     _isWorking = value; 
     OnPropertyChanged("IsWorking"); 
     } 
    } 

    // button click event gets processed here 
    public ICommand GoCommand { get; private set; } 
    private void OnGoCommand(object obj) 
    { 
     if (_selectedCustomer != null) 
     { 
     // wait cursor ON 
     IsWorking = true; 
     _ds = OrdersManager.LoadToDataSet(_selectedCustomer.ID); 
     OnPropertyChanged("GridData"); 

     // wait cursor off 
     IsWorking = false; 
     } 
    } 
} 
+5

Agregue un comentario para su voto abajo para ayudarme a entender por qué. Utilicé este código varias veces con buenos resultados. – Zamboni

+0

Aunque creo que no es la solución más simple, tampoco entiendo el voto negativo –

+4

Tampoco obtengo el voto a favor. Esta respuesta es útil cuando usa MVvM (por lo que no hay código subyacente) y desea controlar el cursor para un control específico. Muy útil. –

4

Si la aplicación utiliza la materia asíncrono y que está jugando con el cursor del ratón, es probable que desee hacerlo sólo en el hilo principal de la interfaz de usuario . Puede usar hilo despachador de aplicación para eso:

Application.Current.Dispatcher.Invoke(() => 
{ 
    // The check is required to prevent cursor flickering 
    if (Mouse.OverrideCursor != cursor) 
     Mouse.OverrideCursor = cursor; 
}); 
Cuestiones relacionadas