David
David

Reputation: 581

RX throttle on WinRT TemplatedControl

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.

Upvotes: 0

Views: 295

Answers (2)

Ana Betts
Ana Betts

Reputation: 74682

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.

Upvotes: 1

Asti
Asti

Reputation: 12687

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)))

Upvotes: 1

Related Questions