Click here to Skip to main content
13,705,949 members
Click here to Skip to main content
Add your own
alternative version

Stats

78.4K views
2.4K downloads
97 bookmarked
Posted 18 Jul 2016
Licenced CPOL

MVVM for beginners

, 19 Jul 2016
Rate this:
Please Sign up or sign in to vote.
Introduction to MVVM for absolute beginners

Introduction

This article is here to help people go from absolute beginner to normal beginner with MVVM.

 

Remark this article assume minimal familiarity with XAML and a UI library using XAML such as WPF. Just that you can read it is good enough. Why? Because before XAML MVVM was not possible (without lots of extra work) as summarily explained below.

Remark(2) the code sample is using some C# 6 functionality (null conditiona a?.b, auto property initializer) and some .NET 4.5 one (CallerMemberAttribute) hence one will need at least VS2015 to compile it. But hey, it's free!

Background

Today there are many articles on MVVM. Unfortunately they are often long and complex. This article attempt to be the simplest article you can find on that topic, as well as showing why you would even like to try.

So what is MVVM?  MVVM is a technique to write UI that could be described as follow:

Short explanation 1

M is for Model, this is your data. That is what people were using before MVVM. V is for view, that's what we are gonna make easier to write. VM is for ViewModel, i.e. Model for the View, aka a model that you write specifically to make MVVM work. Plus it might contains view specific property, like "selected item".

Short explanation 2

It's a code technique where one describe the UI like a user would, there is a button here and it does that, there is a text box there and it's the user name... And one can write the UI pretty much like that.. and it all magically work!

So why many MVVM article are so long? Well it's because MVVM DOES NOT just work. One need to use it wih a UI library that enables it. WPF+XAML was the first such framework. Even so, as people get familiar with MVVW, there were many things which still didn't quite work. Most of the articles about MVVM are either large business samples or about filling the missing pieces. This article will NOT do that. It will show a very basic business app with the out of the box tools. But this is the reason why MVVM started at WPF.

Lastly MVVM apply to developing "Data Views", such as "UserView" or "SchoolList", but is not good for low level control such as DatePicker, or TextBox.

MVVM Etymology

After some debate here on the CP forums, I want to add this etymology section. While it will have absolutely no impact on your understanding and implementation of MVVM as a GUI coding technique, it might smooth over some discussion that you might have on the topic.

MVVM is only a GUI coding technique which helps make GUI coding simpler and more efficient. But it was born as an entreprise developement pattern. In such environment it is common place to have many layers. For example data exchange layer, business layer, etc...

When MVVM was born, with its many UI interfaces (most notably INotifyPropertyChanged, INotifyColllectionChanged, ICommand) it was apparent that a new layer was needed. It is the ViewModel layer, that implement those interfaces.

The M in MVVM is an umbrella term for all those enterprise layers that are totally irrelevant to MVVM as a GUI coding technique. It is only there to be opposed by the VM, ViewModel, which is what matter for MVVM, and for this article.

Let's get started

Start visual studio (VS2015 is a free download) and the menu File > New > Project > Windows > WPF Application.

Open MainWidow.xaml and add replace the <Grid> tag with:

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <DockPanel>
            <TextBlock Text="Added Names" DockPanel.Dock="Top" Margin="5,3"/>
            <ListBox></ListBox>
        </DockPanel>

        <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" Width="5" Background="Gray" HorizontalAlignment="Left" />

        <Grid Grid.Column="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <TextBlock Grid.Row="0" Text="Name" Margin="5,3"/>
            <TextBox Grid.Row="0" Grid.Column="1" Margin="5,3"/>

            <TextBlock Grid.Row="1" Text="Your name is:" Margin="5,3"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Margin="5,3"/>
            
            <Button Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" Content="Add Me" Margin="5,3" MinWidth="75" />

        </Grid>

    </Grid>

If you run the app (F5) you should see the following:

When one click the button or edit the text box, nothing happen! Let's fix that.

First ViewModel

Let's write a model (all in one Model - View Model really, it's fine to do so) that nicely represent the intent of this form.

    public class Model
    {
        public string CurrentName { get; set; }
        public List<string> AddedNames { get; } = new List<string>()
    }

And now we have to associate this model with the view. This is done through the DataContext property.

DataContext is a special property that will flow through all element of the visual tree. Set it on the MainWidow, it will be available to all control. Let's set it in the window constructor as follow:

        public MainWindow()
        {
            InitializeComponent();
            DataContext = new Model();
        }

And let's update the text box and label to reflect the CurrentName model property and the list box to be the AddedNames property. Below is the new XAML for the view, with change in bold.

            <TextBlock Text="Added Names" DockPanel.Dock="Top" Margin="5,3"/>
            <ListBox ItemsSource="{Binding AddedNames}">

<!--
   .......
-->

            <TextBlock Grid.Row="0" Text="Name" Margin="5,3"/>
            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding CurrentName, UpdateSourceTrigger=PropertyChanged}" Margin="5,3"/>

            <TextBlock Grid.Row="1" Text="Your name is:" Margin="5,3"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding CurrentName}" Margin="5,3"/>

We made usage of this Binding XAML Markup extension. MarkupExtension are special way to provide value programatically and must be written between curly brace {}, Binding is central to MVVM functionality and is the magic sauce that synchronize your UI with your model.

Run the app and... Nothing has changed!

The problem here is while the CurrentName property does change the UI doesn't know it does. Hence doesn't update

Notify Change Interfaces

Enter INotifyPropertyChanged and INotifyCollectionChanged interfaces. ViewModels should implement these interfaces to notify the UI they have changed.

With that in mind here is the new code for our model:

    public class Model : INotifyPropertyChanged
    {
        #region CurrentName

        public string CurrentName
        {
            get { return mCurrentName; }
            set
            {
                if (value == mCurrentName)
                    return;
                mCurrentName = value;
                OnPropertyChanged();
            }
        }
        string mCurrentName;

        #endregion

        public ObservableCollection<string> AddedNames { get; } = new ObservableCollection<string>();


        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged([CallerMemberName]string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

If the app is run now, the label will update live as we change the value in the TextBox! Those two controls are now "magically" synchronized through Binding. The binding will propagate change to and from the TextBox to the model's property. And from the model's property to the TextBlock any time it changes for any reason.

Pro Tip Writing a model property that fire a PropertyChangedEvent, even if only 8 lines long, can become quite tedious. Reduce your error rate by using snippets!

AddedNames is now an an ObservableCollection, an out of the box implementation of IList and INotifyCollectionChanged.

ICommand

Button and other actionable items (such as MenuItem) work through an interface named ICommand. One glaring omission of WPF is that it doesn't provide an out of the box, simple, model friendly ICommand implementation. One could refer here to the seminal RelayCommand by Josh Smith... but since it's a very simple interface and I don't want to use any third party, we are just going to implement it inline.... Add this to the Model class:

        public Model()
        {
            AddCommand = new AddNameCommand(this);
        }

        class AddNameCommand : ICommand
        {
            Model parent;

            public AddNameCommand(Model parent)
            {
                this.parent = parent;
                parent.PropertyChanged += delegate { CanExecuteChanged?.Invoke(this, EventArgs.Empty); };
            }

            public event EventHandler CanExecuteChanged;

            public bool CanExecute(object parameter) { return !string.IsNullOrEmpty(parent.CurrentName); }

            public void Execute(object parameter)
            {
                parent.AddedNames.Add(parent.CurrentName); ;
                parent.CurrentName = null;
            }
        }

        public ICommand AddCommand { get; private set; }

Needless to say, this ICommand could be generalized... I left it as an exercise to the reader as they say!

And now let the button know about the command with that simple xaml change (in bold).

            <Button Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Left" Content="Add Me" 

                    Command="{Binding AddCommand}"/>

Run the app, test....

Woot... The Add button now automaticall enable / disable if the text box is empty, add the name to the list and reset the text box!

 

  • Congratulation, you have now written an MVVM app! :)
 

Most of the procedural code was in the model. The UI was just declarative XAML that synchronize with the model through Bindings. That is the essence of MVVM.

Advanced Topic: DataTemplate

Below, In that particular section, I won't post full code, I suggest you download the article code and look at the code there.

DataTemplate are XAML fragments can be used by other control to create part of their UI on demand. There is a lot to say about data templates and templates in general. What I want to briefly cover here is how it relates to ItemsControl.

ListBox is an ItemsControl. There are many ItemsControl (TreeView, MenuItem, ListBox, etc...) What they all share is that they display list of items. ItemsControl have an ItemsSource property which must be set to a (possibly observable) list of items, as in (the previous sample):

<ListBox ItemsSource="{Binding AddedNames}">

What if the model in the list is a little bit more complicated as, say:
(getter / setter code ommited, but just the same as CurrentName above)

    public class Person : INotifyPropertyChanged
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;
    }

How will the the ItemsControl (in this case ListBox) know how to display the Persons?
By giving it a DataTemplate that will generate the view for each item! For each of these XAML fragment the DataContext will be the item itself. One can then directly bind to the Person's property.

        <DataTemplate x:Key="PersonTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding LastName}" FontWeight="Bold" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding FirstName}"/>
            </StackPanel>
        </DataTemplate>

<!--
   ....
-->
            <ListBox ItemsSource="{Binding AddedPersons}"

                     ItemTemplate="{StaticResource PersonTemplate}">
            </ListBox>

Giving us

Voila!

You know enough to start using MVVM now. And make your overall UI code both more simple and more dynamic. Happy coding! :)

History

This is the first version. I don't expect any more...

License

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

Share

About the Author

Super Lloyd
Software Developer (Senior) http://www.ansibleww.com.au
Australia Australia
The Australia born French man who went back to Australia later in life...
Finally got over life long (and mostly hopeless usually, yay!) chronic sicknesses.
Worked in Sydney, Brisbane, Darwin, Billinudgel, Darwin and Melbourne.

You may also be interested in...

Comments and Discussions

 
QuestionMessage Removed Pin
5-Sep-17 18:37
memberyashwant mahajan5-Sep-17 18:37 
QuestionWhat did you mean with this? Pin
Ibn Sani Al-Wadiyyah14-May-17 20:43
memberIbn Sani Al-Wadiyyah14-May-17 20:43 
AnswerRe: What did you mean with this? Pin
Super Lloyd15-May-17 2:25
memberSuper Lloyd15-May-17 2:25 
GeneralRe: What did you mean with this? Pin
Ibn Sani Al-Wadiyyah15-May-17 5:28
memberIbn Sani Al-Wadiyyah15-May-17 5:28 
GeneralRe: What did you mean with this? Pin
Super Lloyd15-May-17 5:39
memberSuper Lloyd15-May-17 5:39 
PraiseRe: What did you mean with this? Pin
Ibn Sani Al-Wadiyyah15-May-17 7:35
memberIbn Sani Al-Wadiyyah15-May-17 7:35 
QuestionHELP URGENTLY WITH THE VARIABLE ADDEDNAMES Pin
14-Mar-17 0:55
member14-Mar-17 0:55 
AnswerRe: HELP URGENTLY WITH THE VARIABLE ADDEDNAMES Pin
Super Lloyd14-Mar-17 4:33
memberSuper Lloyd14-Mar-17 4:33 
QuestionHELP URGENTLY WITH THE Pin
14-Mar-17 0:03
member14-Mar-17 0:03 
PraiseFive Stars! Pin
Togr82b4gotn23-Aug-16 6:30
memberTogr82b4gotn23-Aug-16 6:30 
GeneralRe: Five Stars! Pin
Super Lloyd23-Aug-16 15:17
memberSuper Lloyd23-Aug-16 15:17 
GeneralComment Pin
kalsa22-Aug-16 19:35
memberkalsa22-Aug-16 19:35 
GeneralRe: Comment Pin
Super Lloyd22-Aug-16 19:41
memberSuper Lloyd22-Aug-16 19:41 
QuestionHow did this get best article Pin
Clifford Nelson22-Aug-16 11:50
memberClifford Nelson22-Aug-16 11:50 
AnswerRe: How did this get best article Pin
Super Lloyd22-Aug-16 17:34
memberSuper Lloyd22-Aug-16 17:34 
AnswerRe: How did this get best article Pin
Clifford Nelson27-Aug-16 7:53
memberClifford Nelson27-Aug-16 7:53 
GeneralRe: How did this get best article Pin
Super Lloyd28-Aug-16 17:17
memberSuper Lloyd28-Aug-16 17:17 
QuestionVote of 5 Pin
Kenneth Haugland30-Jul-16 17:53
professionalKenneth Haugland30-Jul-16 17:53 
AnswerRe: Vote of 5 Pin
Super Lloyd30-Jul-16 18:07
memberSuper Lloyd30-Jul-16 18:07 
GeneralRe: Vote of 5 Pin
Kenneth Haugland30-Jul-16 18:19
professionalKenneth Haugland30-Jul-16 18:19 
GeneralRe: Vote of 5 Pin
Super Lloyd30-Jul-16 18:34
memberSuper Lloyd30-Jul-16 18:34 
GeneralRe: Vote of 5 Pin
Pete O'Hanlon8-Sep-16 23:07
protectorPete O'Hanlon8-Sep-16 23:07 
GeneralMy vote of 5 Pin
habe828-Jul-16 8:03
memberhabe828-Jul-16 8:03 
GeneralRe: My vote of 5 Pin
Super Lloyd29-Jul-16 16:57
memberSuper Lloyd29-Jul-16 16:57 
PraiseForget all the controversy! Pin
Jim Meadors21-Jul-16 19:10
memberJim Meadors21-Jul-16 19:10 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180920.1 | Last Updated 20 Jul 2016
Article Copyright 2016 by Super Lloyd
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid