Click here to Skip to main content
15,881,089 members
Articles / Desktop Programming / WPF

MVVM - Sample application with a focus in property-changed event

Rate me:
Please Sign up or sign in to vote.
4.82/5 (12 votes)
24 Sep 2017CPOL5 min read 24.8K   468   11   6
A clarification of property-changed interface and where/when to be implemented.

Introduction

There are many discussions and arguments with regards to whether a Model should implement a property-changed interface or not. In theory, implementing a property-changed interface (INotifyPropertyChanged) in the Model violates the MVVM pattern. This article presents when that scenario usually happens and tries to solve it in order to keep the MVVM pattern valid and reliable.

Background

First, a short overview of the MVVM pattern. I will not go in much detail here since lots has been written on the web and in books. The point of this article is to clarify the property-changed interface, so if you are totally unfamiliar with the MVVM pattern I would then suggest you start with the basics before proceeding here. My source of information is the article of Robert McCarter, Design Patterns - Problems and Solutions with Model-View-ViewModel.

Image 1

Model - Simple class objects that hold data and sometimes contain logic. Model does not directly reference View or in other way, Model has no dependency on view as how they are implemented. Technically speaking, Model classes are used in conjunction with a service or a repository that encapsulate data access.

ViewModel - Main purpose of ViewModel classes is to expose data to the View. It includes presentation logic and can be tested independently of Model. Similar to Model, ViewModel never references View but it exposes properties and commands to bind the View data. In essence, ViewModel acts as a coordinator between View and Model.

View - There should be no logic code in View classes. Views are meant only for UI visual behavior.

The challenge

The MVVM pattern suggests not mixing UI code (Presentation Logic) and data code (Business Logic), the aim is to separate those two domains. The standard MVVM approach is to implement property-changed interface only on the ViewModel. Therefore, if the interface is implemented in the Model, it does violate the pattern and pumps up the Model.

However, if a Model class has 20 properties that need to be exposed in the View, the ViewModel typically ends up having 20 identical properties that simply proxy the call to the underlying Model instance. These proxy properties usually raise a property-changed event when set to indicate to the View that the property has been changed. Therefore, every Model property that needs to be exposed in the View, should have a proxy property. But assuming that a professional application might have multiple Model classes that need to be exposed to the View through the ViewModel, this makes the implementation a nightmare. In that case, many developers end up adding the property-changed event in the Model classes. With this approach, the Model becomes complex and it reduces your ability to introduce new ViewModel-specific functionality later.

Using the code

The idea of the application is very simple. There is only one window divided in two sections, section 1 and section 2. The section 1 contains 16 controls, the half of them are texts and the rest are circles which play the role of green and red light. Finally, a "Generate" button generates some values. These values are numbers which come from a mock-service that generates them in the range of -100 to 100. If the generated random number is positive then its corresponding light becomes green. Accordingly, if the number is negative the light becomes red.

The section 2 is just a simple sum calculator. The user inputs the numbers and the "Sum" button returns the result. There is not any complex logic here, just a sum fucntion.

Image 2

The current sample uses the MVVM Light Toolkit which is a lightweight toolkit that speeds up the development of MVVM applications.

Now, let's see a common mistake while building the Section 1. Several developers used to implement the RaisePropertyChanged in the Model. When the View binds directly to the Model you are mixing UI code and data code. The result is like this:

// Model 
public class Section1Model : ViewModelBase
{
	private int _value1;
	private SolidColorBrush _lightIndicator1;
	// ...
		
	public int Value1
	{
		get
		{
			return _value1;
		}
		set
		{
			_value1 = value;
			RaisePropertyChanged("Value1");
		}
	}

	// ... more proxy-proterties here

	public SolidColorBrush LightIndicator1
	{
		get
		{
			return _lightIndicator1;
		}
		set
		{
			_lightIndicator1 = value;
			RaisePropertyChanged("LightIndicator1");
		}
	}

	// ... more proxy-proterties here
}

As it is already mentioned, that violates the MVVM pattern. I know sometimes it might be useful but in similar cases like this one, we can find -and I will present it later- a better way that adheres to the MVVM pattern. The next image shows how the pattern's architecture looks like.

Image 3

In addition, I would like to mention another mistake which is against best practice in MVVM. This is the usage of the SolidColorBrush. Once again, it is a UI-related duty (visual behavior) and it should reside within the View.

Now, what I prefer to do in order to skip the implementaion of the property-changed event in the Model, is to create a wrapper for the Model in the ViewModel and bind its properties in the XAML. Here is an example, starting from the updated Model:

// Model 
public class Section1Model
{
	private int _value1;
	private SolidColorBrush _lightIndicator1;
	// ...
		
	public int Value1
	{
		get
		{
			return _value1;
		}
		set
		{
			_value1 = value;
		}
	}

	// ... more proxy-proterties here

	public SolidColorBrush LightIndicator1
	{
		get
		{
			return _lightIndicator1;
		}
		set
		{
			_lightIndicator1 = value;
		}
	}

	// ... more proxy-proterties here
}

And now the ViewModel implements the property-changed event.

// ViewModel
public class MainViewModel : ViewModelBase
{
	private Section1Model _section1Model;
	
	public Section1Model Section1Model
	{
		get
		{
			return _section1Model;
		}
		set
		{
			_section1Model = value;
			RaisePropertyChanged("Section1Model");
		}
	}
}

Finally the bindings in the XAML become:

// View
<TextBlock Text="{Binding Path=Section1Model.Value1, Mode=OneWay, FallbackValue=value1}">/>
<Ellipse Fill="{Binding Path=Section1Model.LightIndicator1, FallbackValue=Gray}">/>

Now to make the Model clean from UI-related code, we drop the SolidColorBrush. We can create our own type for representing the color in Model (e.g. bool). Then we can write a custom ValueConverter to convert Model color type into presentation framework dependent color representation. Converter should be used together with bounding expression. An example can be found here.

// Custom ValueConverter
class BoolToColorConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		if (value == null)
		{
			return new SolidColorBrush(Colors.Gray);
		}

		return System.Convert.ToBoolean(value) ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
	}

	public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
	{
		throw new NotImplementedException();
	}
}

The bool plays the role of the green/red light (true/false). A clean Model looks like this:

// Model 
public class Section1Model
{
	private int _value1;
	private bool _lightIndicator1;
	// ...
		
	public int Value1
	{
		get
		{
			return _value1;
		}
		set
		{
			_value1 = value;
		}
	}

	// ... more proxy-proterties here

	public bool LightIndicator1
	{
		get
		{
			return _lightIndicator1;
		}
		set
		{
			_lightIndicator1 = value;
		}
	}

	// ... more proxy-proterties here
}

The bindings in XAML become:

// View
<src:BoolToColorConverter x:Key="boolToColorConverter"/> // adding the resource
<Ellipse Fill="{Binding Path=Section1Model.LightIndicator1, Converter={StaticResource boolToColorConverter}, FallbackValue=Gray}">/>

Now the application cleanly separates the business and presentation logic from its user interface.

The Section 2 doesn't necessarily needs the creation of a Model. Its purpose in this sample is just to give you a comparison to Section 1. Obviously, if you need to make a real calculator, you might need a Model class, or a Service that calculates all the functions. It is up to you.

Conclusion

Once again, it is not always bad to implement the property-changed event in the Model. But if you need a clean MVVM pattern you must separate them. The benefits of a clean MVVM pattern are:

  • It provides separation of concerns. A clean separation between application logic and the UI will make an application easier to test, maintain, and evolve.
  • It is a natural pattern for XAML platforms.
  • It enables a developer-designer workflow. When the UI XAML is not tightly coupled to the code-behind, it is easy for designers to exercise the freedom they need to be creative and make a good product.
  • It increases application testability.

License

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


Written By
Software Developer
Greece Greece
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralModel and ViewModel link Pin
Member 975439525-Jan-18 9:09
professionalMember 975439525-Jan-18 9:09 
GeneralLost fine-grained control over properties Pin
Wiktor Wandachowicz26-Jun-17 21:13
professionalWiktor Wandachowicz26-Jun-17 21:13 
GeneralRe: Lost fine-grained control over properties Pin
G. Tsepas26-Jun-17 21:23
G. Tsepas26-Jun-17 21:23 
QuestionMagic strings and other issues Pin
George Swan23-Jun-17 20:09
mveGeorge Swan23-Jun-17 20:09 
AnswerRe: Magic strings and other issues Pin
Member 1247590010-Jul-17 22:00
Member 1247590010-Jul-17 22:00 
AnswerRe: Magic strings and other issues Pin
G. Tsepas7-Jan-18 22:48
G. Tsepas7-Jan-18 22:48 

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.