Probablemente te has encontrado el siguiente problema más de una vez: digamos que tienes una caja de texto, y quieres mostrar sugerencias con respecto a lo que el usuario está ingresando, al momento en el que lo escribe. Lo más probable es que hayas usado el evento KeyDown para este fin, con algo de esta forma, que consume un webservice:
1 2 3 4 |
txtOrigen.KeyDown += async (sender, args) => { await App.ViewModel.Buscar(txtOrigen.Text); }; |
Esto funciona muy bien para uno, dos o tres caracteres. Pero si el texto es más largo, y el usuario escribe rápido, vas a tener una sucesión de eventos KeyDown, y cada uno de éstos llamará a tu webservice. Nada te asegura que las respuestas de tu webservice lleguen en secuencia, por lo que finalmente puedes estar mostrando las sugerencias de una consulta anterior, lo que es una pésima experiencia de usuario.
Este mismo problema te puede ocurrir, por ejemplo, al moverte por un mapa, si estás haciendo una consulta a un backend que te trae los pins u objetos que quieres mostrar en el mapa. Evidentemente, no te interesa mostrar los resultados de las consultas anteriores, te interesa mostrar el de la última consulta, lo que está mirando el usuario *ahora*.
Para solucionar este problema muy común, te sugiero usar Reactive Extensions. Esta librería te permite solucionar este y muchos otros problemas. El concepto básico es que tendrás que modificar un poco tu código para utilizar CancellationToken, un objeto que envías al método que realiza las consultas, para notificarle cuándo quieres cancelar una consulta y descartar las respuestas obtenidas. Recuerda que no te interesan las respuestas salvo la última.
Primero instala Reactive Extensions desde NuGet (instalar Rx-Main instala las otras dependencias):
Luego, reemplaza la asignación de KeyDown por lo siguiente, en el constructor de tu página (esto es para TextBox de Windows 8):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var queryTextChangedObservable = Observable.FromEventPattern<KeyEventHandler, KeyRoutedEventArgs> (s => txtOrigen.KeyDown += s, s => txtOrigen.KeyDown -= s); queryTextChangedObservable .Throttle(TimeSpan.FromMilliseconds(750)) .Scan(new { cts = new CancellationTokenSource(), e = default(EventPattern<KeyRoutedEventArgs>) }, (previous, newObj) => { previous.cts.Cancel(); return new { cts = new CancellationTokenSource(), e = newObj }; }) .ObserveOn(SynchronizationContext.Current) .Subscribe(async s => { var textBox = (TextBox)s.e.Sender; App.ViewModel.Buscar(textBox.Text, s.cts.Token); }); |
Se lee un poco complicado pero en el fondo no lo es. Las novedades son las siguientes:
Obviamente todo depende del tipo de evento y los parámetros que quieras usar. Personalmente lo estoy usando en TransantiagoMaster para los eventos ViewChangeEnded en Windows8, ViewChangeEnd en Windows Phone 7 y CenterChanged en Windows Phone 8.
0 Comment