Frage

I have some problems with thread synchronization in my Templated control (trying to do a AutoComplete control)

Inside my control I have this 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);
                                                                          }
                                                                      }));
        }
    }

And it is complaining that inside my ShowProgressBar method I'm trying to access code that was marshalled by another thread.

If I comment out the Throttle and the ObserveOn(Dispatcher) it works just fine, but it does not throttle my service calls as I want to.

If I only comment out the Throttle part, Nothing happens at all.

War es hilfreich?

Lösung

Asti's got the right idea, but a far better approach would be to provide the IScheduler argument to Throttle instead:

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

This will make operations below it happen on the UI thread (including your ShowProgressBar), up until the SelectMany.

Andere Tipps

Every dependency object requires that any changes to dependency properties be made only on the Dispatcher thread. The Throttle is using a different scheduler, hence making any changes to the UI in a subsequent Do combinator would result in an access exception.

You can resolve this by:

  1. Add an ObserveOnDispatcher before any actions which cause side-effects on the Dispatcher. Optionally use another scheduler down the pipeline.
  2. Use Dispatcher.Invoke to execute side-effects through the dispatcher. E.g., .Do(() => Dispatcher.Invoke(new Action(ShowProgressBar)))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top