I am using MVVM with ReactiveUI. I have a property in the model that I want to display and be able to edit in the UI. Is there any simple way to do this using ReativeUI? The following properties should be fulfilled:
- The model property implements INotifyPropertyChanged
- The model property can be changed from the view model or from the model
- Updates from within the model can be made on any thread
- Updates from the view model should use a throttle so that not every keystroke becomes a model update
- The application can be run with a UI or command line only, and the code should also be runnable in unit tests and integration tests
- When using an UI, the PropertyChanged event of the ViewModel needs to be raised on the UI thread
- The throttle can't be blocking in either run mode
- The code should be robust, i.e. not have the risk of causing deadlocks or reverting back to old values.
I somehow imagined this would be a standard case of how to wire a view model to a model but haven't managed to get this to work, and can't really figure out any way to make it work without quite a lot of code for a seemingly simple task.
Sample code of a non-working implementation:
public interface IModel : INotifyPropertyChanged
{
string MyProperty { get; set; }
}
public class SomeViewModel : ReactiveObject
{
private readonly IModel model;
public SomeViewModel(IModel model)
{
MyProperty = String.Empty;
this.model = model;
var inputThrottleTime = TimeSpan.FromMilliseconds(500);
var scheduler = RxApp.MainThreadScheduler is AvaloniaScheduler
? RxApp.MainThreadScheduler
: RxApp.TaskpoolScheduler;
// This doesn't work. If updates are made in the model inputThrottleTime apart, the old value might be reassigned to the model.
// And also, WhenAnyValue shouldn't be used to listen on properties that might be updated on background threads according to ReactiveUI devs.
this.WhenAnyValue(x => x.model.MyProperty).ObserveOn(scheduler).Subscribe(p => MyProperty = p);
this.WhenAnyValue(x => x.MyProperty).Skip(1).Throttle(inputThrottleTime, scheduler)
.Subscribe(p => model.MyProperty = p);
}
[Reactive] public string MyProperty { get; set; }
}