3

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.

Phillip Ngan
  • 15,482
  • 8
  • 63
  • 79
Onur
  • 5,017
  • 5
  • 38
  • 54

3 Answers3

2

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}"/>
Pellared
  • 1,242
  • 2
  • 14
  • 29
Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44
  • Does `ReactiveViewModel` need to implement `INotifyPropertyChanged`? Is not the `Reactive` already implementing `INotifyPropertyChanged`? And what about `ValueA`? – Pellared Nov 28 '15 at 20:50
  • Would it not be possible to make a `ReactiveProperty` for `ValueA` in `ReactiveViewModel` and then Subscribe a delegate that would set its value to the `ReactiveModel`? – Pellared Nov 28 '15 at 20:59
  • 1
    @Pellared thanks for comments, i removed INotifyPropertyChanged from my answer. – Dmitrii Dovgopolyi Nov 28 '15 at 21:19
  • This `valueB.Subscribe(model.UpdateB);` was the missing piece. Thanks. – Onur Nov 30 '15 at 12:35
0

This is a bit of a contentious topic but I personally don't see property change notification as being specific to the view model and view, I therefore use B but I add INPC to the models as well in my data layer. This can be done in a post-processing build step using Fody or by wrapping the models in a proxy using something like Castle Dynamic Proxy. I personally use the latter, although it requires integration with your ORM so as to not hammer performance i.e. you don't want your database code loading a model object and then thinking that object has changed because you've tried to update it use the proxy wrapper (this is especially true when you turn IList<> into an ObservableCollection).

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Just to get it right: For read/write properties you use no reactive approach but events? Whether this event is added programatically or with Fody is secondary. – Onur Nov 24 '15 at 08:58
-2

Your current approach doesn't seem to make a lot of sense. You are implementing events to signal when the Model changes so the View Model can take action. However only the View Model should change the Model, therefore events are completely unnecessary.

The View Model is responsible for making changes to the Model, therefore it should know when a change has been performed, as it is the source of said change.


A pure MVVM approach would be something like this:

public class MyModel
{
    public string MyValue { get; set; }

    ...
}

public class MyViewModel
{
    private MyModel _Model;

    public string MyModelValue
    {
        get { return _Model.MyValue; }
        set 
        {
            _Model.MyValue = value;
            //Notify property changed.
        }
    }

    ...
}

It is not the responsibility of the Model to notify the View of changes, instead it is the responsibility of the ViewModel to signal these changes. The Model should not be exposed to the View, but instead the properties of the Model that the View requires should be exposed.

Think of it this way.

  • The user changes the MyModelValue property in a TextBox on the View.
  • The View notifies the ViewModel of the change.
  • The ViewModel changes the Model.

The only purpose of INotifyPropertyChanged is when the above process is reversed, where the ViewModel needs to tell the View that a property has changed:

  • A method in the ViewModel is called that updates MyModelValue.
  • The ViewModel notifies the View of the change.
  • The View updates the TextBox.

The pattern of exposing only properties of the Model that the view requires is not always followed, instead you may see the entire Model being exposed to the View, but as I have said many times before, MVVM is a pattern, not the law. Implementing INotifyPropertyChanged in the Model is perfectly acceptable.

Mike Eason
  • 9,525
  • 2
  • 38
  • 63
  • I think you got me wrong. I don't intend to expose the model directly to the view. Sometimes the model changes "by itself" (e.g. receives data from a different machine, a filesystem watcher, a TCP connection, ...) or is changed by a second view model also manipulating the same instance of the model (e.g. two windows: one containing a list of customers, one a detail view of a single customer and changes in the detail view need to be visible in the list). In these cases I need a way to signal the view model from the model so that the view model itself can notify the view. – Onur Nov 25 '15 at 09:00
  • Perhaps your *service*, i.e filewatcher could update the **view model** property instead of the model directly? This will ensure that the view gets updated by the view model. This could save you from having to tie a complicated web of events. – Mike Eason Nov 25 '15 at 09:05
  • What about the case where two view models access the same instance of the model? I'd also like to stuff as much as possible into the model, as long it's not directly related to view stuff. – Onur Nov 25 '15 at 09:32
  • Then I believe you may have no choice but to implement events within your model. – Mike Eason Nov 25 '15 at 11:11
  • Yes. Btw. my original intent was to tranform this "event in model" to something using reactive extensions... – Onur Nov 25 '15 at 11:57