2010-09-18 19 views
10

In WPF a causa delle complessità relative all'aggiornamento dell'interfaccia, a volte devo eseguire azioni dopo un breve intervallo.Inoltro invio ritardato?

Attualmente sto facendo questo semplicemente: (?)

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

Ma è sia un po 'brutto e forse troppo in alto per creare un nuovo timer ogni volta Qual è il meglio da un punto di vista delle prestazioni a fallo, cioè esegui più prontamente? E ciò che è buon modo per riscrivere il codice di cui sopra in qualcosa di simile:

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

Dove Timespan è il ritardo, Grazie per ogni ingresso :)

+0

Invece di aspettare che qualcosa prima di aggiornare, non puoi spostare la responsabilità per aggiornare l'interfaccia utente alla cosa che stai aspettando? Questo eviterebbe i timer. – x0n

+0

sfortunatamente no, sono cose da hacky come aggiornare l'interfaccia utente e nasconderlo dopo un breve ritardo in modo che la finestra venga aggiornata quando viene mostrata di nuovo – Homde

risposta

11

non Supporrei che il DispatcherTimer è pesante ... perché non scrivere semplicemente un metodo di estensione su Dispatcher che consente di utilizzare la sintassi che si desidera e utilizza uno DispatcherTimer sullo sfondo? Io personalmente chiamare DelayInvoke piuttosto che BeginInvoke però ... e mi piacerebbe anche risolvere il problema di usare sempre Action piuttosto che un delegato arbitraria ... che renderà più facile da usare espressioni lambda:

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

(Tendo a scoprire che è più leggibile se le funzioni anonime vengono utilizzate come argomento finale in una chiamata di metodo, ma è solo una preferenza personale.)

Dato che è probabile che tu voglia utilizzare il millisecondo la maggior parte del tempo, potrebbe avere anche un altro metodo di supporto:

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

Un'altra alternativa da provare è semplicemente utilizzare il metodo esistente BeginInvoke ma con una priorità molto bassa, in modo che il delegato venga richiamato solo dopo che tutto il resto è terminato. Senza conoscere i dettagli della tua situazione, è difficile sapere se funzionerà o meno, ma vale la pena provarci.

+0

Un metodo di estensione sarebbe molto bello, ma non significherebbe che dovrei prima fare un initinvoke sul dispatcher di essere sul filo giusto e quindi impostare il timer, che è un po 'più in testa di avere solo il timer? Potrebbe non essere un problema però ... – Homde

+0

@MattiasK: Perché dovrebbe farlo? Non avrebbe * davvero * bisogno di interagire con il dispatcher ... sarebbe solo per il senso di coerenza. Devo ammettere che non so cosa succede se crei un DispatcherTimer da un thread diverso ... ma se funziona direttamente, funzionerà da un metodo di estensione senza problemi aggiuntivi. –

+2

@MattiasK: ho appena controllato, e puoi dire al costruttore di DispatcherTimer su quale dovrebbe essere eseguito il Dispatcher, quindi dovresti usare quel sovraccarico dal metodo di estensione e specificare il Dispatcher che era stato passato al metodo. –

0

Potrebbe essere possibile utilizzare la classe DelayBinding di Paul Stowell da qui: http://www.paulstovell.com/wpf-delaybinding.

Con questa capacità è possibile eseguire il binding a una proprietà di dipendenza in una classe in grado di eseguire l'azione quando la proprietà viene modificata. Il binding gestirà il ritardo. Potrebbe essere particolarmente piacevole se si dispone di un design di tipo MVVM.

3

Un .NET 4.5 modo:

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

non sono affezionato alla DispatcherTimer perché non v'è alcun modo semplice per passare parametri a non attraverso la funzione portata/chiusure.

Invece io uso un Task.Delay() e avvolgere che in un metodo Dispatcher.Delay() estensione:

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

Per chiamare questo allora:

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