Daca suntem intr-o aplicatie MVVM folosind Siverlight, in unele situatii o sa avem nevoie Dispatcher pentru a executa cod pe UI thread.
O varianta este de a trasmite o instanta la Dispatcher prin constructor sau cand se face apelul la o metoda data.
O varianta este sa ne definim o interfata pentru dispatcher-ul de UI, prin intermediul careia sa se poata rula cod pe UI thread. In acest mod, nu o sa mai fie coupled dispatcher-ul de restul aplicatiei. Iar in cazul in care dorim ca codul sa fie executat pe un alt thread sau in alt mod o sa fie nevoie sa ne implementam diferit acesta clasa( de exemplu pentru teste).
O varianta este de a trasmite o instanta la Dispatcher prin constructor sau cand se face apelul la o metoda data.
public class Context()
{
...
Context(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
}
..
{
_dispatcher.BeginInvoke( () => { ... } );
}
}
O alta varianta destul de des intalnita este sa se obtina o referinta la Distapcher direct prin apelul:Deployment.Current.Dispatcher
Ambele variante functioneaza fara nici o problema. Problema este ca clasele care folosesc acest Dispatcher o sa fie coupled. Aceasta problema se poate observa cel mai usor in momentul in care se scriu teste. Daca testele o sa reluze din brower de exemplu, pana la sfarsitul testelor, threadul de UI o sa fie blocat de rularea acestora, din aceasta cauza nu o sa se ajunga ca codul apelat prin Dispatcher sa fie executat. Pe langa acest lucru in teste dorim sa putem controla cand se ruleaza acest cod.O varianta este sa ne definim o interfata pentru dispatcher-ul de UI, prin intermediul careia sa se poata rula cod pe UI thread. In acest mod, nu o sa mai fie coupled dispatcher-ul de restul aplicatiei. Iar in cazul in care dorim ca codul sa fie executat pe un alt thread sau in alt mod o sa fie nevoie sa ne implementam diferit acesta clasa( de exemplu pentru teste).
public interface IUiDispatcher
{
void Invoke(Action action);
}
public class UiDispatcher : IUiDispatcher
{
private readonly Dispatcher _dispatcher;
public UiDispatcher(Dispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void InvokeOnUiThread(Action action)
{
// Verificam daca suntem pe UI thread.
if( _dispatcher.CheckAccess())
{
action.Invoke();
}
else
{
_dispatcher.BeginInvoke(action);
}
}
}
Enjoy!
Aha - asa se intampla intotdeauna cand nu se lucreaza test-first, desi pare mai greu la inceput..
ReplyDeleteSi in WPF sau WinForms se intampla la fel: lumea folosea o varianta de ModelViewPresenter in speranta ca ulterior sa poata testa persenter-ul, doar ca sa descopere ca s-a grabit cineva si afisa un message box direct, dintr-o gramada de presentere - sa vezi refactoring dupa cand trebuiau scrise testele.. :)
Cat traim invatam.
ReplyDelete