5

In my ViewModel I'll often have code like the following:

public bool IsDownloading { get { return _isDownloading.Value; } }
private ObservableAsPropertyHelper<bool> _isDownloading;

...

FileDownloader.WhenAny(x => x.IsDownloading, x => x.Value)
   .ToProperty(this, x => x.IsDownloading, out _isDownloading, false, RxApp.MainThreadScheduler);

Note the last RxApp.MainThreadScheduler argument - that makes sure that all updates to the IsDownloading property happen on the main UI thread.

The reason for that is because of what might subscribe to it. For example, in my view xaml I might have the following code:

this.OneWayBind(ViewModel, x => x.IsDownloading, y => y.DownloadProgress.IsActive);

Here you see that IsDownloading is going to be routed to the IsActive item on a control - so any updates must occur on the UI thread. So all of this works.

But I don't get why ReactiveUI was designed this way. It seems like it is important to the View, not the VM, that the updates occur on the UI thread, so shouldn't Bind or OneWayBind make sure it is on the right thread? In fact, if you look at the XML documentation for those routines, they talk about attaching things to View's, so the implication is that it should always be on the main thread.

So: Why doesn't OneWayBind and Bind implicitly force the MainThread? Have I missed something in how ReactiveUI works (or can be made to work)?

Salah Akbari
  • 39,330
  • 10
  • 79
  • 109
Gordon
  • 3,012
  • 2
  • 26
  • 35
  • Yea just ran into this on an iOS app... My guess is that it doesn't want to be overly opinionated because you can bind to pretty much anything that's a property in the view class which might be something you don't want set on the uithread? It seems like 95 percent of the time though you'd want this on the UIThread... Maybe an optional parameter on the bind to indicate the scheduler would be helpful to handle edge cases and then just have it default to UIThread – Shane Neuville Sep 23 '15 at 19:10
  • This was exactly my thinking; which made me wonder if perhaps I'd missed something in the architecture. – Gordon Sep 23 '15 at 19:12
  • 1
    I found this comment on github https://github.com/reactiveui/ReactiveUI/issues/798 "Bind will not automatically move stuff to the UI thread for you, because of performance" so I'm guessing the FW wants to just leave you with that burden so it doesn't overly burden the UI Thread without your permission... I might just start doing all the property bindings like you have above just to be safe :-) – Shane Neuville Sep 23 '15 at 19:14
  • Ah ha! Good find. I suspect the reason is that the BindTo method gets used to bind to things that don't need the UI thread, and that gets built up to be used for UI related stuff (like OneWayBind and Bind). Ok, if that is the case, then perhaps the right way to approach this is to make a OneWayBindUI method. Then I can take all my ObserveOn out of my VM (which shouldn't have UI in it) and move it into the View... and now the threading model remains contains (and assured) in one place: the View. Since you found this, @ShaneNeuville, feel free to write up a short answer below along these lines! – Gordon Sep 23 '15 at 19:21

0 Answers0