2010-09-18 10 views
10

En WPF debido a las complejidades de cómo se actualiza la interfaz, a veces tengo que realizar acciones después de un breve retraso.¿Invocación retrasada del envío?

Actualmente estoy haciendo esto simplemente por: (?)

 var dt = new DispatcherTimer(DispatcherPriority.Send); 
     dt.Tick += (s, e) => 
     { 
      dt.Stop(); 
      //DoStuff 
     }; 
     dt.Interval = TimeSpan.FromMilliseconds(200); 
     dt.Start(); 

Pero es tanto un poco feo y tal vez demasiado trabajo para crear un nuevo contador de tiempo cada vez Cuál es la mejor desde el punto de vista de rendimiento para hacerlo, es decir, ejecutar más rápidamente? Y lo que es una buena manera de reescribir el código anterior en algo así como:

 this.Dispatcher.BeginInvoke(new Action(delegate() 
     { 
      //DoStuff 
     }), DispatcherPriority.Send,TimeSpan.FromMilliseconds(200)); 

Dónde Timespan es el retraso, Gracias por cualquier entrada :)

+0

En lugar de esperar a que algo antes de actualizar, no puedes mover la responsabilidad para actualizar la interfaz de usuario a lo que está esperando? Esto evitaría temporizadores. – x0n

+0

desafortunadamente no, es algo raro como actualizar la interfaz de usuario y esconderla después de un breve retraso para que la ventana se actualice cuando se muestre de nuevo – Homde

Respuesta

11

no asumiría que el DispatcherTimer es de alto gramaje ... ¿por qué no simplemente escribir un método de extensión en Dispatcher que le permite usar la sintaxis que desea, y usa un DispatcherTimer en segundo plano? Yo personalmente llamarlo DelayInvoke en lugar de BeginInvoke aunque ... y también me fijarlo a utilizar siempre Action en lugar de un delegado arbitraria ... que hará que sea más fácil de usar expresiones lambda:

Dispatcher.DelayInvoke(TimeSpan.FromMilliseconds(200),() => { ... 
}); 

(Tiendo a encontrarlo es más fácil de leer si las funciones anónimas se usan como el argumento final en una llamada a método, pero eso es solo una preferencia personal)

Dado que es muy posible que quiera usar milisegundos la mayor parte del tiempo, podría tener otro método de ayuda también:

Dispatcher.DelayInvokeMillis(200,() => { ... 
}); 

Otra alternativa para intentar es simplemente usar el método existente BeginInvoke pero con una prioridad muy baja, por lo que su delegado solo se invocará después de que todo lo demás haya terminado. Sin conocer los detalles de su situación, es difícil saber si funcionará o no, pero vale la pena intentarlo.

+0

Un método de extensión sería muy bueno, pero ¿no significaría que primero tendría que hacer un begininvoke en el despachador para estar en el hilo correcto y luego configurar el temporizador, que es un poco más sobrecarga que solo tener el temporizador? Puede que no sea un problema ... – Homde

+0

@MattiasK: ¿Por qué tendría que hacer eso? No * realmente * necesitaría interactuar con el despachador en absoluto ... eso sería solo por la sensación de coherencia. Es cierto que no sé qué sucederá si crea un DispatcherTimer a partir de un hilo diferente ... pero si funciona cuando se hace directamente, funcionaría desde un método de extensión sin ningún problema adicional. –

+2

@MattiasK: Acabo de verificar, y puede decirle al distribuidor DispatcherTimer en qué despachador debe ejecutarse, para que use esa sobrecarga desde el método de extensión y especifique el distribuidor que se le pasó al método. –

0

Es posible que pueda utilizar la clase DelayBinding de Paul Stowell desde aquí: http://www.paulstovell.com/wpf-delaybinding.

Con esto puede enlazar a DependencyProperty en una clase que puede ejecutar la acción cuando la propiedad cambia. El enlace administrará el retraso. Puede ser particularmente agradable si tiene un diseño de tipo MVVM.

3

A .NET 4,5 manera:

public async void MyMethod() 
    { 
     await Task.Delay(20); 
     await Dispatcher.BeginInvoke((Action)DoStuff); 
    } 
0

no soy aficionado a la DispatcherTimer porque no hay manera fácil de pasar parámetros en que no sea a través de alcance/cierres de función.

En vez utilizo un Task.Delay() y envolver en un método que Dispatcher.Delay() extensión:

public static class DispatcherExtensions 
{ 

    public static void Delay(this Dispatcher disp, int delayMs, 
          Action<object> action, object parm = null) 
    { 
     var ignore = Task.Delay(delayMs).ContinueWith((t) => 
     { 
      disp.Invoke(action, parm); 
     }); 
    } 

    public static void DelayWithPriority(this Dispatcher disp, int delayMs, 
         Action<object> action, object parm = null, 
         DispatcherPriority priority = DispatcherPriority.ApplicationIdle) 
    { 
     var ignore = Task.Delay(delayMs).ContinueWith((t) => 
     { 
      disp.BeginInvoke(action, priority, parm); 
     }); 

    } 

    public static async Task DelayAsync(this Dispatcher disp, int delayMs, 
         Action<object> action, object parm = null, 
         DispatcherPriority priority = DispatcherPriority.ApplicationIdle) 
    { 
     await Task.Delay(delayMs); 
     await disp.BeginInvoke(action, priority, parm); 
    } 
} 

Para llamar a esto, entonces:

Dispatcher.Delay(4000, (win) => 
{ 
    var window = win as MainWindow; 
    window.ShowStatus(null, 0); 
},this); 
Cuestiones relacionadas