Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / WPF
Tip/Trick

Generic MVVM Data Exchange between Model and ViewModel

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
31 Jul 2019CPOL2 min read 16.2K   11   9
Make data exchange in MVVM easy on yourself

Introduction

I've been developing a tool to help the other devs on the team deal with the creation of menu items for their MVC apps. The tool in question is a WPF desktop app that utilizes the MVVM pattern. I was writing the models and associated view models when it struck me that I'd been doing it "the hard way" all these years. I was writing a series of methods *in every viewmodel* that shuttled data back and forth between the model and the view model. For some of the models, this involved 20 or more properties, and the coding required to support them was pure drudgery. Yesterday, I came up with an alternative.

The Code

I created a class with the following code, and inherited that class from my view model classes:

C#
public enum MVVMDirection { FROM, TO };

/// <summary>
/// ViewModel base class
/// </summary>
public class VMBase
{
    /// <summary>
    /// Move the data from the model to the viewmodel, using reflection. 
    /// Property names in both objects MUST be the same (both name and type)
    /// </summary>
    /// <typeparam name="TModel">The model's type</typeparam>
    /// <param name="model">The model object the data will be moved from</param>
    public void UpdateFromModel<TModel>(TModel model)
    {
        this.Update<TModel>(model, MVVMDirection.FROM);
    }

    /// <summary>
    /// Move the data from the viewmodel to the model, using reflection. 
    /// Property names in both objects MUST be the same (both name and type)
    /// </summary>
    /// <typeparam name="TModel">The model's type</typeparam>
    /// <param name="model">The model object the data will be moved from</param>
    public void UpdateToModel<TModel>(TModel model)
    {
        this.Update<TModel>(model, MVVMDirection.TO);
    }

    /// <summary>
    /// Update to or from the model based on the specified direction. Property names in both 
    /// objects MUST be the same (both name and type), but properties used just for the view 
    /// model aren't affected/used.
    /// </summary>
    /// <typeparam name="TModel">The model's type</typeparam>
    /// <param name="model">The model object the data will be moved to/from</param>
    /// <param name="direction">The direction in which the update will be performed</param>
    public void Update<TModel>(TModel model, MVVMDirection direction)
    {
        PropertyInfo[] mProperties  = model.GetType().GetProperties();
        PropertyInfo[] vmProperties = this.GetType().GetProperties();

        foreach(PropertyInfo mProperty in mProperties)
        {
            PropertyInfo vmProperty = this.GetType().GetProperty(mProperty.Name);
            if (vmProperty != null)
            {
                if (vmProperty.PropertyType.Equals(mProperty.PropertyType))
                {
                    if (direction == MVVMDirection.FROM)
                    {
                        vmProperty.SetValue(this, mProperty.GetValue(model));
                    }
                    else
                    {
                        vmProperty.SetValue(model, mProperty.GetValue(this));
                    }
                }
            }
        }
    }
}

Example Usage

As with any code, there is more than one way to use these methods. In my own case, my typical ViewModel object contains a property that represents the model, and the constructor takes a model object parameter. After setting the Model property from the parameter, the constructor performs an initial call to the UpdateFromModel() method. My view models look something like this:

C#
// ViewModelBase contains the methods that are discussed in this tip, 
// and in my case, ViewModelBase inherits from 
// a class called Notifiable which inherits from INotifyPropertyChanged 
// and contains properties to support using view 
// models in a WPF environment.
public partial class MyViewModel : ViewModelBase 
{
    private string property1;

    public MyModel Model { get; set; }

    public string Property1 
    { 
        get { return this.property1; }
        set
        {
            if (this.property1 != value)
            {
                this.property1 = value;
                // in the "Notifiable" class
                this.NotifyPropertyChanged();
            }
        }
    }

    public MyViewModel(MyModel model)
    {
        this.Model = model;
        // in the ViewModelBase class
        this.UpdateFromModel<mymodel>(this.Model);
    }
}

I use a Model property because I don't want to commit changes to the model's data until the user is ready to do so (determined by actions in the UI). Of course, you could directly update the model's value from the viewmodel property (via app UI actions) if that's what you need to do, but that's not the way *I* do it. As for the data transfer between object properties, you can likely imagine a long list of this kind of code, you can probably understand why I came up with these base class methods.

I understand that some of you reading this may have a different outlook on life as it relates to where you put code and how you implement the MVVM pattern, so here's an example of doing it a little different, you can also perform that initial method call externally of the viewmodel object after instantiating the view model, like so:

C#
// create and populate your model however you want/need to do it
MyModel modelObj = new MyModel();

// do something to populate your model if necessary (my model objects are 
// created and populated via calls to the database)

// create your viewmodel
VMMyModel vmObj = new VMMyModel();
// and move the model data to the viewmodel
vmObj.UpdateFromModel<MyModel>(modelObj);

// change the viewmodel property values
// and then move it back to the model
vmObj.UpdateToModel<MyModel>(modelObj);

// save your model to the data source from which it was retrieved
modelObj.CallYourSaveMethod();

I can't possibly conjure up a definitive list of use cases, and I'm honestly not interested in religious discussions regarding implementation styles, so I hope you get the idea of how to utilize the code. Go forth, and mould it into your own image.

Change History

  • 2019.08.03 - Fixed the Update method to remove references to the viewmodel parameter

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
SuggestionCode improvement : Handle list <->observable collections Pin
Member 1505861726-Feb-21 2:20
Member 1505861726-Feb-21 2:20 
QuestionA slightly Different Approach Pin
George Swan4-Aug-19 3:28
mveGeorge Swan4-Aug-19 3:28 
AnswerRe: A slightly Different Approach Pin
#realJSOP4-Aug-19 4:21
mve#realJSOP4-Aug-19 4:21 
QuestionProblem compiling the class Pin
CWizard2-Aug-19 15:56
CWizard2-Aug-19 15:56 
AnswerRe: Problem compiling the class Pin
#realJSOP3-Aug-19 1:18
mve#realJSOP3-Aug-19 1:18 
AnswerRe: Problem compiling the class Pin
#realJSOP3-Aug-19 1:24
mve#realJSOP3-Aug-19 1:24 
QuestionThank you Pin
MadMyche1-Aug-19 11:22
professionalMadMyche1-Aug-19 11:22 
AnswerRe: Thank you Pin
#realJSOP1-Aug-19 14:00
mve#realJSOP1-Aug-19 14:00 
AnswerRe: Thank you Pin
#realJSOP1-Aug-19 23:17
mve#realJSOP1-Aug-19 23:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.