Project was built with VS 2013. The code is fully commented, so you can just go through it and it'll be easy to figure out what's happening where.
Feel free to download and play with the code.
If you do, and you find it helpful, please come back and vote or comment. :)
Note: I've updated the code. Now you can get the code only (2MB), or just download the executables to see the results (no code, 440Kb). (These are for MVVM Light 4.x.x)
Note 2 (29th October): I've updated the Binaries and code sample for MVVM Light 5. Read the notes at the end for changes.
This should get rid of a case where you downloaded the code without the executables and it was missing some reference DLLs it needed.
Note 3 (21st April, 2016): Updated the source code, you can get it in the V2 zip.
Introduction
If you ever wanted to start a little project (or a big one), and got annoyed with the plumbing involved with the whole WPF-MVVM plumbing involved, this article is for you.
If you're new to MVVM, and want to have a quick look at what you can achieve, by all means, grab the code, and read through it. It is fully commented, and covers lots of different topics.
So, What Do We Have Here?
Most of the things aren't my own inventions, but what you have here is a whole lot of different things together under the same roof, so when you want to start something, you can quickly use this and just replace what you need or remove what you won't use.
Here's a quick overview of what's Included:
- MVVM Light & .NET 4.5
- INPC implementation in base view model.
- Menu
- Tab Control (MVVM + binding of tap per View)
- Enumerations
- Random data for mocking and prototyping
- Using Commands to bind buttons to View Model
- Enable / Disable buttons
- Validations (Text boxes / input fields)
- Converters
- Styles
- DataTemplates
- Resources (images, txt)
- Extension methods
- Random helpers
- DataService & DI (from MVVM Light)
- Unit Testing
You'll probably not use all of these, but it's so much easier to just have it there and cut out what's not needed, than try to remember how to make Resharper play nice with MVVM Light, how to make RaisePropertyChanged
work without supplying it the name of the property as a string
, which files need to be referenced, and so on.
I'm In, What's Next?
Download the code, run the solution, and start hacking away. In the following sections I'll highlight what's included, why is it there, and how to use it if needed.
The Red Pill or the Blue Pill?
I'm a Grasshopper:
Please, show me the ropes, hold my hand and take me for a stroll around.
--> Start with the background.
I'm a Ninja:
I've been collecting Microsoft MVP's when you were still struggling to write your first "Hello World!
".
--> Jump to what's interesting in each part.
What's all this MVVM I keep hearing about?? Well, as the picture to the right shows, it's a pattern that will decouple your views from your model, and make everything easy to unit test. In other words, less time fixing broken code, and more time sunbathing in Hawaii.
The benefits of MVVM (amongst others, are the power binding from the View (in XAML) to the ViewModel (your C# code), which is intended to free coders from how the design works, and designers from how the code works. You can prototype what you want, tell your dev guy: I want a class with a property Foo
of type int
, and Bar
of type string
. Then you go to your design guy and tell him: I want a page that looks like that, here is what you'll bind to: a Property Foo
of type int
, and Bar
of type string
.
My only issue with WPF/MVVM is the amount of plumbing necessary to start coding. Common examples would be:
- How do I add a Validation rule ...
- How do I define a Converter ...
- What was the command syntax again ...
- How do I bind to ViewModel ...
- How does the Messenger works ...
Usually, once I'm past those, I'll spend some time mocking data for the Design Time, and so on. You should get the point by now ... lots of plumbing ...
General Structure
- Auxiliary (top level for all misc folders)
- Converters
- Helpers (Extensions methods, etc.)
- Resources (Files, dlls, pictures, etc.)
- Validations
- Design Time (Central folder for all design time files)
- Models
- ViewModels
- Views
Climbing Down the (Project) Tree
Properties
I've added the Annotations.cs to the Properties, in order to facilitate calling the RaisePropertyChanged
without specifying which property is changing. The reason is to avoid simple yet annoying and hard to find bugs that happen when you decide to change your property name, but forget to change the name on the RaisePropertyChanged string
parameter.
These are basically classes that inherit from IValueConverter
. The idea is simple, you supply the Convert
method with the input, and you get to control the Output.
What is this useful for?
- You have a
DateTime
Property and you want to display only the year. - You have a number property for letting the user know how many Unicorns he has, and you want to be able to say "
No Unicorns
", "A Unicorn
", "Some Unicorns
" depending on that number.
For example:
class CommentsConverter : IValueConverter
{
public object Convert(object value,Type targetType,object parameter,CultureInfo culture)
{
var input_string = value as string;
if (input_string.IsNullOrEmpty())
return "The cake is a lie.";
if (input_string.Contains("unicorn"))
return "Warning: Wild Unicorns found on premise.";
return "This following text is Unicorn free: [" + input_string + "]";
}
public object ConvertBack
(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
To use the above converter, have this in your XAML:
<TextBlock
Text="{Binding SomeProperty, Converter={StaticResource CommentsConverter}}"
/>
Helpers
Here are all the little classes that help you get your work done, but aren't that vital by themselves.
Enums.cs
You can have all your enumerations in one place, so you don't need to try and find them when you want to have a look at them.
MessengerClasses
This is used with MVVM Light in order to send Messages from one view to another. This class simply holds together a collection of specific classes that you will send as messages. Have a look at the code for fully documented examples.
Random_Helper
If you're sick of writing: Random rand= new Random();
before you can call rand.GetNext();
to get some random number (and hopefully not forget not to do this in a tight loop because you'll get the same number), I present: The Random_Helper
.
It has all the random methods you can shake a stick at, and then some more. The benefit is that you can use them, add to them, or remove what you don't like, instead of creating them yourself to get some random data.
All are documented, so have a look in there. Some of the methods are:
RandomName
RandomDate
RandomPhone
RandomWeather
RandomBool
StringExtensionMethods
If you hate to write: string.IsNullOrEmpty(some_string)
, and love Linq syntax like: some_string.IsNullOrEmpty()
, you'll love these.
Extensions methods are a basically syntactic sugar that allow you to access static
methods as if they were part of a class usual methods/properties. Head to MSDN documentation if you never heard about them.
Validation_helper
The idea behind this is the assumption that your validations need to do repetitive things in multiple validations (different classes). We can move all the duplicated code to a static helper, and simply call the methods from it.
Resources
All of your misc files will probably end up here. In this example, I have a picture, a text document, my styles and datatemplates, and another class (ApexGrid
) that I'll be using as well.
ApexGrid.cs
Normal XAML grids suck. Apex Grid will allow you to replace the following code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
</Grid>
with:
<a:ApexGrid Rows="Auto,Auto,*,28" Columns="Auto,200" >
</a:ApexGrid>
DataTemplates.cs
This is where we'll glue our ViewModel
s to our View
s. Have a look at the source code for more examples and options to do some old style templating:
<DataTemplate DataType="{x:Type vm:About_ViewModel}">
<views:About_View />
</DataTemplate>
Styles.cs
One point to have all our styles declared together, in order to give the application a consistent look, and to avoid writing repetitive code that ends up breaking and out of sync.
<Style TargetType="Label" x:Key="MyLabelTemplate">
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="Margin" Value="5"></Setter>
</Style>
Similar to the converters, these are classes that extend ValidationRule
. They will take an object, run some logic, and return a true
/false
result, which then might trigger a style if you want (In this template, it'll have a blinking red exclamation mark, which you can find in the "Styles.cs" if you want to see how it's implemented.
class Example_Validation : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var str = value as string;
var sb = new StringBuilder();
var valid = true;
if (str.Length < 5)
{
valid = false;
sb.AppendLine("String too short.");
}
return new ValidationResult(valid, sb.ToString());
}
}
Design Time
You can put all your design time classes here. In this template, I'm just reusing Laurent Bugnion and his MVVM Light template.
Basically, it holds a design time implementation of the DataService
, and will return back fake local data, instead of the real DataService
that will usually call a database or the web.
Models
Here, you'll have all your model classes. If you're asking what's your model, you should do some reading on MVVM, but basically it's all your logic that interacts with the DB or the Web and doesn't care at all about how you present it to the user.
In this example, you'll find there the IDataService
Interface, the implementation (DataService
), and a Person.cs class, which I'm using to fake people information at, but would come from the database in a real world program.
ViewModels
In here, you'll have all your ViewModels, which sit between the View and the Model, and act like a buffer so you don't mingle your UI with you Logic. All the ViewModels in the template are fully commented, and you can have a read to see what they do if you care.
Quick overview:
- About_ViewModel.cs: My info, but you might want to have something similar
- Main_ViewModel.cs: The properties that the MainWindow will bind to
- MVVMLight_ViewModel.cs: Laurent's example, left it in so you can see what he did
- MyBase_ViewModel.cs: All our custom view models inherit from this, in order to support the INPC "magic"
- Random_ViewModel.cs: First Tab, holds random data
- ValidationsConverters_ViewModel.cs: Second Tab, with examples of validations and converters
ViewModelLocator
: Laurent's Locator. This is used as a static instance to get the other View Models
Views
These are the files that hold the UI. Each View is a combination of a XAML file and a Xaml.cs codebehind file. The only View with code behind is the About, and the reason is that I wanted to link back to CodeProject. Depending on which side of the fence you are on regarding the "pureness" of the MVVM pattern, you might love or hate this.
MainWindow and Main_ViewModel
This is the entry point to our program. The ViewModel (a.k.a: VM) holds other ViewModels as properties, and the View simply bind each TabItem
to a VM.
The window is composed of a DockPanel
, with a Menu
docked to the top, a StatusBar
docked to the bottom, and the last child which is the TabControl
takes the rest of the space.
The MenuItems
in the Menu
use Commands
to bind to either ApplicationCommands
(Cut, Copy, Paste), or to actual methods on other VMs. In order to reach these other VMs, we use MVVM Light'
s messenger class, sending a message of some type, which the other VM will register to receive and route to its own Command.
Look at the code for examples and comments. Here's a snippet:
<Grid x:Name="LayoutRoot">
<DockPanel >
<Menu DockPanel.Dock="Top" ... >
<MenuItem Header="_Default commands" IsTabStop="False">
<MenuItem Command="ApplicationCommands.Copy" />
</MenuItem>
<MenuItem Header="_Custom commands" IsTabStop="False">
<MenuItem Header="Refresh 7 people"
Command="{Binding RefreshPeopleMenu_Command}"
CommandParameter="7" />
</MenuItem>
</Menu>
<StatusBar DockPanel.Dock="Bottom" ...>
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Name="StatBarText" Text="{Binding StatusBarMessage}" />
</StatusBarItem>
</StatusBar>
<TabControl>
<TabItem Header="Random Helper">
<ContentControl Content="{Binding Random_VM}" />
</TabItem>
</TabControl>
</DockPanel>
In the Main_ViewModel
constructor, we're registering to listen to messages of type StatusMessage
, so we can easily set the StatusBar
text from any VM in the application:
Messenger.Default.Register<StatusMessage>(this, msg => StatusBarMessage=msg.NewStatus);
Random_View and Random_ViewModel
Top has a ComboBox
which is bound to an enum
example. Nothing fancy.
The bottom part holds a DataGrid
which holds some random objects of type Person
(you can find the class in the Models folder). Besides binding to the properties of an object and displaying different properties of that object, the interesting thing is the Interaction.Triggers. Have a look at the code for more comments, but basically it uses Blend's DLLs to enable your controls to react to clicks on them.
In our case, double clicking on the DataGrid
will open a MessageBox
with the name of the person you clicked on, but you can do whatever you want of course. As an extra bonus, you get the syntax to pass a parameter with the command (in our example, we bind it to the selected item).
Next, we have an example of how to enable/disable a button using the CanExecute
on the command. In this case, it's a simple check that the parameter passed is bigger than 3, so if you'll choose the value "1
" in the ComboBox
, the button will be disabled.
To make sure that button is disabled as soon as we choose "1
", we'll call the CommandManager.InvalidateRequerySuggested();
on the property it's bound to. This will refresh all the bindings and as a result, check the can execute of that button, and disable it. Look at the code comments for more information.
Just like in the main VM, we register the command to refresh the people with the Messenger
using:
Messenger.Default.Register<RefreshPeople>
(this, (msg) => Execute_RefreshPeople(msg.PeopleToFetch));
This enables us to reuse this command and use it from the Menu
(which is part of the Main_ViewModel
, without hard coding the call).
Last, when the button is clicked, and the command to refresh the people is fired, we are sending a message to the messenger to set the status. I've set that method as static
on the Messenger
class, so you can call it from any VM, passing a string
that you want to set the StatusBar
to. The code for this is:
private void Execute_RefreshPeople(int arg)
{
PeopelCollection = new ObservableCollection<Person>(_dataService.GetPeople(arg));
var msg = arg + " people refreshed.";
StatusSetter.SetStatus(msg);
}
ICommand
Here I'm using a convention that declares & initializes the command at the same time, saving you the need to initialize the command in the constructor. Here's an example:
public ICommand SomeMethod_Command
{
get
{
return _someMethodCommand ??
(_someMethodCommand =
new RelayCommand<int>(Execute_SomeMethod,
CanExecute_SomeMethod));
}
}
private ICommand _someMethodCommand;
ValidationsConvertes_View and ValidationsConvertes_ViewModel
Validations
The idea behind these is to let the user know something is wrong with is input. You can use this to validate emails, make sure some fields are not empty and so on. It's been quickly covered above. In my Styles.xaml (under Auxiliary\Resources), I've defined a style called "myErrorTemplate" which targets "Control
" (basically, everything). The idea is to use this is a base for other styles (in my case, for TextBox
, CheckBox
and ComboBox
), so we'll get the same style applied to all input fields. It'll flash an exclamation mark three times and set the border of the control to red, so the user knows something is wrong. It also sets the tool tips with helpful messages to allow the user to understand what's the problem when he hoover over the control.
So, the top part with the 2 input fields takes care of that.
Converters
The second part covers converts. It's been also covered above, so I'll be brief.
You have one TextBox
in which you can write, and two TextBlocks
that will show you in real time what the converter gets, and what it returns. There are three states this converter will react to: empty string, and whether or not the string contains the word "unicorn
". Feel free to play around with it and look at the code.
Button Enable/Disable Example
Yes, I know, you're ninjas and you might have noticed this has been done already in the Random_ViewModel
. Congratulations. Now keep reading.
As opposed to doing the check and setting the status in the viewmodel
(code), here it's done via the XAML, using Style.Triggers
.
The idea is you can take your control, and declare in the XAML what happens when certain events are true
or false
. You can set a single trigger, or multiple triggers (in the code you have both examples, so please look at it for more).
Here's an example with a single trigger on a button. If a property called ShouldBeEnabledProperty
on the ViewModel
is true
, the button is enabled. If it's false
, it'll be disabled. You can trigger this in the code, and assuming your property is an INPC and it fires a property changed, it'll automatically update the button to the right state when it happens.
<Button Content="Shiny name here"
Command="{Binding Path=Some_Command}" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShouldBeEnabledProperty}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
ICommand
This ViewModel
uses a different convention for defining and initializing a command in the View Model. Declare a simple ICommand
Auto Property, and then initialize it in the constructor like this:
public Your_ViewModel(IDataService dataService)
{
...
SomeMethod_Command = new RelayCommand(Execute_SomeMethod, CanExecute_SomeMethod);
...
}
public ICommand SomeMethod_Command { get; private set; }
I suggest picking the one you like best, and sticking with it.
MvvmLight_View and MvvmLight_ViewModel
This is the default template that comes with Laurent Bugnion and his MVVM Light template. I left it as a tab so you can see the Nuget package defaults and look at his code.
I've had the honour of briefly contacting him and he seems like a great guy. He was very very helpful even though he was very busy :).
About_View and About_ViewModel
I created this for a different project, and it's a nice touch if you want to have an "About" view, I think. The ViewModel
is rather boring (one property as a stub for others), since it's not supposed to do much.
The interesting points would be:
- Embedding an image (resource) to your View
- Having a link that will open in a web browser (the computer's default). The gist of this is having an Hyperlink in the View, like this:
<TextBlock >
CodeProject:
<Hyperlink NavigateUri="http://www.codeproject.com/Members/_Noctis_"
RequestNavigate="NavigateToCP">
link
</Hyperlink>.
</TextBlock>
Which will call this method on the code behind, which will start a new process and go to the link:
private void NavigateToCP(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start("http://www.codeproject.com/Members/_Noctis_");
}
Unit Testing
Without Unit testing, I believe your project is doomed to failure, and shouldn't hit production. I've added a simple Unit Test project to the solution, just so you'll have no excuse of setting one yourself. It has some very simple test methods that will fetch some random data from the Random_Helper
class, and do some basic validations.
If you've never done testing, grab some books, and jump on the wagon. It'll change the way you code, and save you heaps of time in the future.
Other Important Parts
ViewModelLocator
This is the heart of the "dynamic" fetching and getting views and view models in MVVM Light. You should remember five things:
- Have this in your App.xaml:
In App.xaml:
<Application.Resources>
<vm:ViewModelLocatorTemplate
xmlns:vm="clr-namespace:MVVM_Template_Project.ViewModels"
x:Key="Locator" />
</Application.Resources>
- Have this in each view, so it'll know its data context:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
- Register the class with the
SimpleIoc
(simple inversion of control) in the ViewModelLocator
constructor:
SimpleIoc.Default.Register<Main_ViewModel>();
- Have a property with the
ViewModel
in the ViewModelLocator
:
public Main_ViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<Main_ViewModel>();
}
}
- Make sure you add an entry to the DataTemplates.xaml in order to bind the
ViewModel
to the View
(as per Digitalbeach request :) ). It should look like:
<DataTemplate DataType="{x:Type vm:Your_ViewModel_Namel}">
<views:Your_View_Name/>
</DataTemplate>
Note: Duplicate Properties in ViewModelLocator
and Main_ViewModel
:
Because I'm not using the default MainWindow
for everything, and because I have the Main_ViewModel
which is used in the ViewModelLocator
, you'll have to define your ViewModels
properties in BOTH ViewModelLocator
AND Main_ViewModel
. They will both return the same Instance, but this is the reason why:
- When you're binding in the VIEW, you're using the
ViewModelLocator
's property. - When you're binding in the
MainWindow
, you're using the Main_ViewModel
property.
For example:
<TabItem Header="Some name">
<ContentControl Content="{Binding Some_VM}" />
</TabItem>
The above code is using the Main_ViewModel
's Some_VM
Property, but when you bind it in the view's XAML:
DataContext="{Binding Test_VM, Source={StaticResource Locator}}"
you're using the ViewModelLocator
property. Feel free to name them differently if it helps you.
If you're not using tabs,or not going to bind things in the main view model, you can remove all the properties from the Main_ViewModel
, and just have them in your ViewModelLocator
.
Annotations.cs
I've added the file Annotations.cs
to the Properties, in order to facilitate calling the `RaisePropertyChanged
` without specifying which property is changing. The reason is to avoid simple yet annoying and hard to find bugs that happen when you decide to change your property name, but forget to change the name on the `RaisePropertyChanged
` string
parameter.
Base Class
Next, I've created MyBase_ViewModel
that extends GalaSoft's ViewModelBase
, with the RaisePropertyChanged
and RaisePropertyChanging
so that the above will work for all classes derived from it. Read: Use MyBase_ViewModel : ViewModelBase
and not ViewModelBase
as the base for your view models.
Application Icon
To set the application icon, simply go to your project's properties, and under the Application tab, set the Icon to any icon that you want (note, it has to be an ".ico" file, but you can easily run a Google search and find free online websites that will convert your .jpg or .png to icons. Here are two:
Wrap Up
This resulted in a much longer task than I've originally intended (believe it or not, I've intended to write a completely different project to begin with) but I believe this will be of much help to developers everywhere.
You can find most of the above plus more and in the code as comments, and in the "Readme.md" as well.
If you've found this article helpful, please vote for the article, leave a message, and feel free to post back to this article. :)
Updates
MVVM-Light 5 (October 29th, 2014)
Laurent Bugnion has updated the MVVM-Light libraries to version 5. There were 2 major (breaking?) changes that I've noticed.
- The
RaisePropertyChanging
was removed (due to moving to portable class library), so it had to be removed from the base class. You can read more about my question and his reply here. - The relay command
CanExecute
was broken (for the same reasons) and a little workaround was needed in order to make it work again. You can read more about it in this thread. The crux of the fix is changing the namespace for that command from "GalaSoft.MvvmLight.Command
" to "GalaSoft.MvvmLight.CommandWpf
".
In any case, if you grab the updated code sample, the library is already updated, and the changes were introduced, so you can just use that :).
5th October, 2014
I've updated the code a bit. The changes are:
- Sealed 3 view models to avoid CA2214 warnings. There are remarks above them, so you can easily know why it was done. You can read more in my answer to the question in the comments here.
- I've updated the MVVM Light and
CommonServiceLocator
to get rid of a warning that was displayed with resharper (everything still worked though). It should build all clean and nice now. - As a result, I've uploaded the code again, so the downloads are different.
History
- 21st April, 2016: Fixed some typos in the source code, reworded some comments, simplified some boolean logic, added a section to the
ViewModelLocator
in the article as per comment request & uploaded the new code. (It has some notes on using string interpolation, assuming you're using the latest and greatest C#.) - 29th October, 2014: Updated MVVM-Light to version 5
- 5th October, 2014: Updated libraries and code
- 4th May, 2014: Initial release