Search code examples
c#mvvmsystem.reactivereactiveuireactive-property

How to make the transition from "traditional" to reactive MVVM


I want to make a transition to a reactive view model / model.

I've used 3 scenarios so far:

  • "ValueA": The model value is only accessed from one view model at a time and the value is only changed through the view model

    => simple property in model, forwarding property with PropertyChanged in view model

  • "ValueB": The model value is accessed from several view models and/or changes from other sources

    => property with event in model, forwarding property and translation from changed event to PropertyChanged in view model

  • "ValueC": A value only used in the view model

    => no property in model, property backed by own field with PropertyChanged in view model

This is my "current" approach:

class Model
{
    public string ValueA {get;set;}

    private string valueB;
    public event ValueBChangedEvent ValueBChanged;
    public string ValueB
    {
        get
        {
            return valueB;
        }
        set
        {
            valueB = value;
            ValueBChanged();
        }
    }
}

class ViewModel : INotifyPropertyChanged
{
    private Model model;

    public string ValueA
    {
        get 
        {
            return model.ValueA;
        }
        set
        {
            model.ValueA = value;
            OnPropertyChanged();
        }
    }

    ViewModel()
    {
        model.ValueBChanged += model_ValueBChanged;
    }

    private void model_ValueBChanged()
    {
        OnPropertyChanged("ValueB");
    }


    public string ValueB
    {
        get 
        {
            return model.ValueB;
        }
        set
        {
            model.ValueB = value;
            // no change notification since done via model
        }
    }   


    private string valueC;
    public string ValueC
    {
        get 
        {
            return valueC;
        }
        set
        {
            valueC = value;
            OnPropertyChanged();
        }
    }       
}

This is how I intend to model them using reactive extensions:

class ReactiveModel
{
    public string ValueA {get;set;}

    private ISubject<string> valueB = new Subject<string>();
    public ISubject<string> ValueB
    {
        get
        {
            return valueB;
        }
    }
}

class ReactiveViewModel : INotifyPropertyChanged
{
    private ReactiveModel model;

    public string ValueA
    {
        get 
        {
            return ???;
        }
        set
        {
            ???
        }
    }


    private ReactiveProperty<string> valueB = model.valueB.ToReactiveProperty();
    public Reactive<string> ValueB
    {
        get 
        {
            return valueB;
        }
        // no setter since access via ValueB.Value which is read-write
    }   


    private ISubject<string> _valueC = new Subject<string>();
    private ReactiveProperty<string> valueC = _valueC.ToReactiveProperty();
    public ReactiveProperty<string> ValueC
    {
        get 
        {
            return valueC;
        }
        // no setter since access via ValueC.Value which is read-write
    }       
}

Summary:

  • "ValueA": I have no clue for this case
  • "ValueB": works at first glance but does neither propagate changes from view model to model nor the other way.
  • "ValueC": this works as intended

I'd be happy if I had a solution for ValueA and ValueB.


Solution

  • ValueB: View model is responsible for updating model. ReactivePropertyuses only IObservable interface from your model properties and reads values from ValueB(does not write anything). ReactiveProperty is changed by view through Value property. ReactiveProperty implements IObservable and you should subscribe to changes to get new values.

    ValueA: We can make a ReactiveProperty on the view model side an subscribe to propagate the changed value to the model.

    Here is the code for the solution:

    class ReactiveModel
    {
        public string ValueA {get;set;}
        private readonly Subject<string> valueB = new Subject<string>();
        public IObservable<string> ValueB
        {
            get
            {
                return valueB;
            }
        }
    
        public void UpdateB(string newValue)
        {
            valueB.OnNext(newValue);
        }
    }
    
    class ReactiveViewModel
    {
        private readonly ReactiveModel model;
        private readonly ReactiveProperty<string> valueA;
        private readonly ReactiveProperty<string> valueB;
    
        public ReactiveViewModel(ReactiveModel model)
        {
            this.model = model;
    
            valueA = new ReactiveProperty<string>(model.ValueA);
            valueA.Subscribe(x => model.ValueA = x);
    
            valueB = model.ValueB.ToReactiveProperty();
            valueB.Subscribe(model.UpdateB);
        }
    
        public IObservable<string> ValueA
        {
            get
            {
                return valueA;
            }
        }
    
        public ReactiveProperty<string> ValueB
        {
            get 
            {
                return valueB;
            }
        }
    }
    

    XAML would be in both cases:

    <TextBox Text="{Binding ValueA.Value, UpdateSourceTrigger=PropertyChanged}"/>