John Jerrby
John Jerrby

Reputation: 1703

Update the model from the view model

Update the model from the view model

I have read some post about the MVVM but I not sure if understand the way that the view model is updating the model

Currently I have two text boxes in the UI which is bound to the XAML view and call to the view model when the event was raised . when should be the place in the view model when I updating the model?

This is the view model

class ViewModel:INotifyPropertyChanged
    {

 private String _url;
 private String _TemplateType;

    public string URL
    {
    get { return _url;  }
        set
        {
            if (value != _url)
            {
                _url= value;
                OnPropertyChanged("URL");
            }
        }
    }

    public string TemplateType
    {
    get { return _TemplateType;  }
        set
        {
            if (value != _TemplateType)
            {
                _TemplateType= value;
                OnPropertyChanged("URL");
            }
        }
    }

The model

internal class DefineAddinModel
{
    public string TemplateType { get; set; }
    public String URL { get; set; }
}

Upvotes: 2

Views: 347

Answers (4)

Stefan Z Camilleri
Stefan Z Camilleri

Reputation: 4076

I think that the correct answer here is 'it depends'.

In most general cases, the advantage of actually using a ViewModel is also to track 'transient state', i.e. the state of an 'edit in progress' operation.

In this particular case, you would not push your changes directly to the Model every time a value is updated, instead you would do this via an 'Update' ICommand implementation that will collect all the data from the ViewModel and push it down to the Model.

This approach gives you many advantages:

  1. The user of the view can change their mind as many times as they want, and only when they are happy will the Model actually get updated with their definitive choices
  2. It greatly reduces the load on your persistence service, since only final changes are pushed through.
  3. It allows you to do final validation on a complete set of values, rather than transient states, and hence reduces programming complexity and overhead.
  4. It also makes your UI far more fluid since all the examples above are pushing updates on the UI Dispatcher, and avoids you having to cater for this via Tasks or other async approaches.
  5. The backing model is never in an inconsistent state, since I would imagine that all values on one View/ViewModel are related, and only make sense when updated together using an ACID approach.

Here's an example of how I'd do it.

public class ViewModel:INotifyPropertyChanged {

    private String _url;
    private String _TemplateType;

    public ViewModel(){
        UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
    }

    public bool OnCanExecuteUpdate(object param){
        // insert logic here to return true when one can update
        // or false when data is incomplete
    }

    public void OnExecuteUpdate(object param){
        // insert logic here to update your model using data from the view model
    }

    public ICommand UpdateCommand { get; set;}

    public string URL{
        get { return _url;  }
        set {
            if (value != _url) {
                _url= value;
                OnPropertyChanged("URL");
            }
        }
    }

    public string TemplateType {
        get { return _TemplateType;  }
        set {
            if (value != _TemplateType) {
                _TemplateType= value;
                OnPropertyChanged("TemplateType");
            }
        }
    }

    ... etc.
}

public class DelegateCommand : ICommand {
    Func<object, bool> canExecute;
    Action<object> executeAction;

    public DelegateCommand(Action<object> executeAction)
        : this(executeAction, null) {}

    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
        if (executeAction == null) {
            throw new ArgumentNullException("executeAction");
        }
        this.executeAction = executeAction;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter) {
        bool result = true;
        Func<object, bool> canExecuteHandler = this.canExecute;
        if (canExecuteHandler != null) {
            result = canExecuteHandler(parameter);
        }

        return result;
    }

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged() {
        EventHandler handler = this.CanExecuteChanged;
        if (handler != null) {
            handler(this, new EventArgs());
        }
    }

    public void Execute(object parameter) {
        this.executeAction(parameter);
    }
}

Upvotes: 1

Robert Levy
Robert Levy

Reputation: 29083

The ViewModel usually acts as a wrapper around the Model and contains a reference to the Model which is can update either in response to commands or automatically in property setters.

UPDATE: Here's an example of having the VM act as a wrapper around the Model. This may seem useless in your example but you will find in many cases the VM's getters/setters need to do some sort of transformation on the values rather than simply passing them through.

class ViewModel:INotifyPropertyChanged
{
 private DefineAddinModel model;

    public string URL
    {
        get { return model.URL;  }
        set
        {
            if (value != model.URL)
            {
                model.url = value;
                OnPropertyChanged("URL");
            }
        }
    }

    public string TemplateType
    {
        get { return model.TemplateType;  }
        set
        {
            if (value != model.TemplateType)
            {
                model.TemplateType = value;
                OnPropertyChanged("TemplateType");
            }
        }
    }

Upvotes: 5

AymenDaoudi
AymenDaoudi

Reputation: 8309

You need to bind your TextBoxes to the two properties URL and TemplateType. Try to use Commands (in the ViewModel)instead of events (in The CodeBehind) since you are in MVVM. For updating the model : use a button with it's Command property bound to OnSave just like this example:

    private String _url;
    private String _TemplateType;
    private DefineAddinModel _defineAddin;

    public DefineAddinModel DefineAddin
    {
        get {return _defineAddin;}
        set
        { 
            _defineAddin = value;
            OnPropertyChanged("DefineAddin");
        }
    }

    public string URL
    {
        get { return _url;  }
        set
            {
                if (value != _url)
                {
                    _url= value;
                    OnPropertyChanged("URL");
                }
            }
    }

    public string TemplateType
    {
        get { return _TemplateType;  }
        set
        {
            if (value != _TemplateType)
            {
                _TemplateType= value;
                OnPropertyChanged("URL");
            }
        }
    }

    public RelayCommand OnSaved
    {
            get;
            set;
    }


    public ViewModel()
    {
        DefineAddin = new DefineAddinModel();
            OnSaved = new RelayCommand(()=>
                              {
                                    DefineAddin.URL  = URL  ;
                                    DefineAddin.TemplateType = TemplateType;
                              });

Think about using third parties like MVVMLight it helps you a lot with MVVM and the helpers around it (Commands, Messenger, ViewModelLocator ...)

Upvotes: 1

seddik
seddik

Reputation: 646

The better way to update your Model Is by using an event, its safer, so choose weather using a button click or lost focus, or whatever you want

void button_click(object sender,eventsarg e)
{
    MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
    MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;

}

but personnaly i Use the following steps :

1- In your ViewModel create a CurrentItem object of type DefineAddinModel and without OnPropertyChanged then bind it to the View(UI) DataContext of the RootElement on the View )

2- for the model I use the INotifyPropertyChanged for each propery

3- after binding the datacontext of your root element to the CurrentItem of your ViewModel then bind just URL and TemplateType properties to your Controls, so any thing changes on the textbox will update CurrentItem properties

you can also chose the type of the binding (On LostFocus, or OnPropertyChanged)

Upvotes: 1

Related Questions