2015-01-19 15 views
7

Nella mia applicazione WPF, ho un gestore di eventi che viene chiamato l'evento MouseEnter del mio elemento dell'interfaccia utente:Throttle un gestore eventi

myUiElement.MouseEnter += myEventHandler 

vorrei strozzare MyEventHandler in modo che non venga chiamato più di una volta al secondo. Come posso fare questo? Rx è l'approccio migliore solo per questo? Sto usando .NET 4.0 se fa la differenza.

Inoltre, ho bisogno di assicurarmi che l'evento MouseLeave venga sempre chiamato prima del successivo evento MouseEnter; devo gestirlo da solo? Oppure il framework è già stato progettato in modo che gli eventi MouseLeave vengano sempre chiamati prima del successivo evento MouseEnter? Cosa succede se ho codice asincrono in questi gestori di eventi?

+1

In realtà non è necessario chiamarlo o è possibile eseguire un ritorno immediato nel metodo se l'ultima chiamata era troppo vicina all'ora corrente? – siride

+0

Volevo limitarlo in modo che non appesantisca troppo il thread nel caso in cui l'utente sputi il ​​mouse su tutto (ho molti elementi dell'interfaccia utente che avranno lo stesso gestore di eventi); ma suppongo di poterlo fare se è più efficiente di quanto effettivamente lo strozzi .. – Darius

+1

qualsiasi meccanismo di limitazione alla fine coinvolgerà un'istruzione if e una scelta per continuare l'esecuzione o abortire. Non puoi davvero aggirarlo. – siride

risposta

6

Utilizzando Rx, si desidera utilizzare the Sample method o Throttle.

Qualcosa del genere dovrebbe funzionare (non testata):

Observable 
    .FromEventPattern<TextChangedEventArgs>(myUiElement, "MouseEnter"); 
    .Sample(TimeSpan.FromSeconds(1)); 
    .Subscribe(x=>...Do Stuff Here...); 

La differenza tra il campione e la valvola a farfalla è che il campione avrà un valore ogni 1 secondo, non importa quando l'ultimo valore è stato preso, mentre Throttle vorrà un valore e quindi attendere un altro 1 secondo prima di prenderne un altro.

Probabilmente dipende da cosa si sono riprese per ...

+0

Perché "Sample" invece di "Throttle"? – McGarnagle

+1

Ho aggiunto una descrizione per Throttle.Forse Throttle è più ciò che l'OP voleva, ma piuttosto che indovinare ho appena dato la descrizione in modo che l'utente finale possa prendere le proprie decisioni –

6

È possibile utilizzare estensioni reattive, ma è possibile farlo con la stessa facilità con un timer.

Impostare una bandiera con un timer. Quando viene attivato l'evento tick del timer, imposta il flag su false, disabilita il timer ed esegui il codice per il tuo evento. Quindi, nei tuoi gestori di eventi di controllo, il codice del gestore viene saltato se il flag è impostato.

bool flag; 
DispatcherTimer timer; 

public constructor() 
{ 
    timer = new DispatcherTimer(); 
    timer.Interval = Timespan.FromSeconds(1); 
    timer.Tick += (s,e) => { 
     flag = false; 
     timer.Stop() 
     DoThrottledEvent(); 
    } 
} 

void mouse_enter(object sender, MouseEventArgs args) 
{ 
    if(!flag) 
    { 
     flag = true; 
     timer.Start(); 
    } 
} 

void DoThrottledEvent() 
{ 
    //code for event here 
} 

estensioni reattivi introduce una dipendenza in più, ma sono un po 'di divertimento. Se sei interessato, fallo!

3

Un altro approccio sarebbe quello di utilizzare un campo privato per tenere traccia del "tempo", quando si è verificato l'ultimo evento del mouse, e solo continuare l'elaborazione se questo il tempo era più di un secondo fa.

DateTime _lastMouseEventTime = DateTime.UtcNow; 

void OnMouseEnter(object sender, MouseEventArgs e) 
{ 
    DateTime now = DateTime.UtcNow; 
    if (now.Subtract(_lastMouseEventTime).TotalSeconds >= 1) 
    { 
     // do stuff... 
    } 
    _lastMouseEventTime = now; 
} 

garantendo la "roba" viene fatto almeno un secondo di distanza, che è quello che penso che stavi chiedendo.

+0

Lo ha usato per un'app mobile, senza Timer e senza niente - furiosamente -. Grazie per la soluzione pronta. – Liz

+1

Differenza con il timer: non attivare l'intervallo (ad esempio 1 secondo) PRIMA di un'azione, mentre questo dice che non si attiva l'intervallo DOPO un'azione. Supponiamo di premere a 't = 0', quindi' t = 2' e quindi 't = 2,1'. Quindi 't = 0' sparerà,' t = 2' sparerà ma 't = 2,1' non sparerà (né would t = 2,2'). Molto spesso vorresti licenziare l'ultimo valore. – EricG

+0

Questo è esattamente il problema con questa soluzione: l'ultimo valore (o posizione del mouse) non verrà elaborato in molti casi. – Oyvind