2012-09-22 4 views
0

J'ai quelques problèmes avec la synchronisation des threads dans mon contrôle basé sur des modèles (en essayant de faire un contrôle de saisie semi-automatique)gaz RX sur WinRT TemplatedControl

A l'intérieur de mon contrôle, j'ai ce code:

protected override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 

     var searchTextBox = GetTemplateChild("SearchTextBox") as TextBox; 
     if (searchTextBox != null) 
     { 
      var searchDelegate = SearchAsync; 

      Observable.FromEventPattern(searchTextBox, "TextChanged") 
       .Select(keyup => searchTextBox.Text) 
       .Where(TextIsLongEnough) 
       .Throttle(TimeSpan.FromMilliseconds(500)) 
       .Do(ShowProgressBar) 
       .SelectMany(searchDelegate) 
       .ObserveOn(Dispatcher) 
       .Subscribe(async results => await RunOnDispatcher(() => 
                     { 
                      IsInProgress = false; 
                      SearchResults.Clear(); 
                      foreach (var result in results) 
                      { 
                       SearchResults.Add(result); 
                      } 
                     })); 
     } 
    } 

Et il se plaint que dans ma méthode ShowProgressBar j'essaie d'accéder au code qui a été rassemblé par un autre thread. Si je commente le Throttle et l'ObserveOn (Dispatcher) cela fonctionne très bien, mais il ne limite pas mes appels de service comme je le veux.

Si je ne fais que commenter la partie Throttle, Rien ne se passe du tout.

+0

J'utilise le RX 2.0 beta – David

+0

Ceci n'est pas une réponse, mais un petit commentaire - Je ne pense pas que vous ayez besoin du 'async' /' await' dans votre code. Rx fait effectivement l'attente pour vous. Essayez-le sans et laissez-moi savoir si cela fonctionne toujours bien. – Enigmativity

+0

True Enigmativity - le code fonctionne sans async/await. – David

Répondre

1

Asti a le bonne idée, mais une approche beaucoup mieux serait de fournir l'argument IScheduler à Accélérateur place:

// NB: Too lazy to look up real name 
.Throttle(TimeSpan.FromMilliseconds(500), CoreDispatcherScheduler.Instance) 

Cela fera des opérations ci-dessous, il arrive sur le thread d'interface utilisateur (y compris votre ShowProgressBar), jusqu'à SelectMany.

+0

Cela a résolu le problème! La syntaxe que j'ai utilisée était .Throttle (TimeSpan.FromMilliseconds (500), CoreDispatcherScheduler.Default) – David

1

Chaque objet de dépendance nécessite que toute modification des propriétés de dépendance be made only on the Dispatcher thread. Le Throttle utilise un planificateur différent, par conséquent toute modification de l'interface utilisateur dans un combinateur Do ultérieur entraînerait une exception d'accès.

Vous pouvez résoudre ce problème en:

  1. Ajouter un ObserveOnDispatcher avant toute action qui provoquent des effets secondaires sur le répartiteur. Utilisez éventuellement un autre planificateur dans le pipeline.
  2. Utilisez Dispatcher.Invoke pour exécuter des effets de bord via le répartiteur. Par exemple, .Do(() => Dispatcher.Invoke(new Action(ShowProgressBar)))
+0

Salut, bien si j'ajoute ObserveOnDispatcher() avant l'appel à Do (ShowProgressBar) le ShowProgressBar n'est jamais invoqué. Dans Win8, le répartiteur est un CoreDispatcher et utilise .RunAsync() que j'utilise dans le code ci-dessus. Mon problème est que le code ne sera jamais invoqué si j'utilise ObserveOnDispatcher() avant cet appel. – David