C# MVVMLight Tutorial 2 – Adding a Page 4 View

Application Description

This MVVM WPF application builds on Tutorial 1 MVVMControlChange by adding a Page4 UserControl that allows the user to navigate between Page2, Page3, and the new Page4.  As a refresher, Tutorial 1 consisted of a WPF Window with a contentControl and two UserControls (Page2 and Page3) that the user can navigate between with Button controls.   Here is the  SOURCE CODE for Tutorial 1.  The contentControls have data bound text blocks and screen changes driven by RelayCommands and Messages.  The RelayCommands and Messages are features of the MVVM Light tool kit by Laurent Bugnion.

High-level Procedure Overview

●  The high-level overview of for this project starts with copying the Tutorial 1 MVVMControlChange folder, adding the Page 4 View, Page 4 ViewModel, and anything else.

●  In the new version of the MvvmControlChange project,  add a Page4 folder to the Content Folder by copying the Page3 Folder and renaming elements to Page4.

STEP BY STEP to Build the Application

●  Make a copy of the Tutorial 1 Mvvm Tut1 ControlChange folder:

●  Paste a copy of the folder into the same root directory:

●  Rename the folder to “Mvvm Tut2 ADD Page 4”

●  Open the solution from within the renamed Mvvm Tut2 ADD Page 4” folder.

●  The first time I tried this I right clicked the Page3 folder, made a copy, pasted it in the Content folder and renamed everything to Page4.  However, the way I refactored it caused some of the class (or/and namespace) names to also be refactored in the Page3 folder inaddition to the Page4 folder files.   I got a lot a red squiggly lines and errors and you can see some notes from that endeavor here.  Although, its a waste of your time, I recommend you continue on…

●  From this point on, it is the SAME as MVVM Tutorial 1 Sectoin 2.b where the folders and files are added for Page2 and Page 3.  I know, somewhat anti-climatic, but I wanted this tutorial separate from the first one so in the future when I need to only add another Page View via a userControl, I can come to this tutorial and have it concise and uncluttered.

●  Right Click on the Content Folder, Add, New Folder.  Add the Page4 Folder.  Then, repeat the process by right clicking on the Page4 folder and adding the View and ViewModel Folders.  See completed step below.

 

●  Add Files to the View and ViewModel folders.

VIEW FOLDER:  Each View folder will receive a MvvmView (WPF) template.  Add Item to View folder: Right Click on target View Folder, Add, New Item, MvvmView (WPF).  Add the suffix ‘View’ to the Page folder name to create the xaml filename.  You will have the following new file name:  Page4View.

VIEWMODEL FOLDER:  Each ViewModel folder will receive a MvvmViewModel (WPF) template.  Add Item to ViewModel Folder:  Right Click on target ViewModel Folder, Add, New Item, MvvmViewModel (WPF).  You will have the following ViewModel name:  Page4ViewModel.

COMPLETED folders with added files looks like this:

LET’S START ADDING CODE!

●  Double Click on the ViewModelLocator.cs File in the ViewModel folder.

First, point to the locations of the ViewModels in the using statements by adding:

using MvvmControlChange.Content.Page4.ViewModel;

Now, register the ViewModels w/ the SimpleIoc in the static ViewModelLocator() Constructor.

SimpleIoc.Default.Register<Page4ViewModel>();

●  ViewModel Properties.  Ensure you have MVVMLight Toolkit Code Snippets installed.  Type in the snippet ‘mvvmlocatorproperty’ and press Tab.

●  Too much code is thrown in, remove the static ViewModleLocator:

1
2
3
4
5
static ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<ViewModelType>();
        }
static ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<ViewModelType>();
        }

●  Change the property snippet to the following:

1
2
3
4
5
6
7
        public Page4ViewModel Page4ViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<Page4ViewModel>();
            }
        }
        public Page4ViewModel Page4ViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<Page4ViewModel>();
            }
        }

●  You are COMPLETE with your ViewModleLocator.cs to this point.   SAVE and close the ViewModleLocator.cs file.

View and ViewModel Interaction

●  The Page4View.xaml file had been added and now the ViewModleLocator.cs file is updated.  However, Page4 is blank.  We will add some text boxes to the usercontrol.  For navigation, we’ll later add buttons to MainPageView to navigate between Page2, Page3, and Page4 views.  Here are the steps to prepare Page4View:

Page4 will be a UserControl, however when we added the file earlier, it was automatically inserted as a window.  Double Click on the file in the solution explorer and change Change <Window to <UserControl and  </Window> to </UserControl>.  I had to remove the code:

1
SizeToContent="WidthAndHeight"
SizeToContent="WidthAndHeight"

If you don’t have the SizeToContent, don’t worry about it, I was antsy and used the mouse to enlarge the window of the original control prior to changing it to a UserControl and Visual Studio automatically added that code.

Add the following overhead code:

1
2
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
xmlns:Custom="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
xmlns:Custom="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Also, in the DataContext line change the ViewModelName to Page4ViewModel (because that’s the correct view model name):

1
 DataContext="{Binding Page4ViewModel, Source={StaticResource Locator}}"
 DataContext="{Binding Page4ViewModel, Source={StaticResource Locator}}"

Before we add Page 4 content, let’s update the user control that we just changed in the Page4View.xaml.cs file.  Open the Page4View.xaml.cs file and…

Add:  using System.Windows.Controls;

Change  public partial class Page4View : Window  to public partial class Page4View : UserControl

Rebuild F6 to remove squigglies, if necessary.  Run F5 if you would like to test as well, it STILL won’t do anything different!!

Add Page4 Content

Return to the Page4View.xaml and inbetween the <Grid> & </Grid> add the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
 
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="15,24,0,12">
            <TextBlock x:Name="InfoTitlePage4"
                       Text="{Binding InfoTitlePage4}"
                       Margin="5,5,0,0"/>
            <TextBlock x:Name="PageTitle"
                       Text="{Binding InfoToUserPage4}"
                       Margin="5,10,0,0" />
        </StackPanel>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="15,24,0,12">
            <TextBlock x:Name="InfoTitlePage4"
                       Text="{Binding InfoTitlePage4}"
                       Margin="5,5,0,0"/>
            <TextBlock x:Name="PageTitle"
                       Text="{Binding InfoToUserPage4}"
                       Margin="5,10,0,0" />
        </StackPanel>

Note there is no text in the TextBlocks yet, we’ll do that now…  Navigate to the Page4ViewModel.cs file and enter the following code after the constructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        public string InfoTitlePage4
        {
            get
            {
                return "Page 4 View";
            }
        }
        public string InfoToUserPage4
        {
            get
            {
                return "Here is text to the user!";
            }
        }
        public string InfoTitlePage4
        {
            get
            {
                return "Page 4 View";
            }
        }
        public string InfoToUserPage4
        {
            get
            {
                return "Here is text to the user!";
            }
        }

To see if your code worked, return to the Page4View.xaml, press F6 to build.  You should see your text from the Page4ViewModel.cs on the form. It should something like this:

 

 Adding Buttons to MainPageView

Now that we have Page4 setup, we still cannot navigate to it and need to add a button and appropriate code for navigation.  I should have put both buttons for Page2 and Page3 on the MainPageView.  Since I didn’t, let’s add buttons for both Page3 and Page4.  But first, I decided to space the buttons in Grid ColumnDefinitions.  I have a blurb below the following code snippet on how to find the CollectionEditor:  ColumnDefinitions, but for expediency sake, Open MainPageView.xaml, copy all the code below and paste over the section including <Grid x:Name =”LayoutRoot”> to </Grid>.  Do not paste over the starting definitions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
   <!--LayoutRoot contains the root grid where all other page content is placed-->
    <Grid x:Name="LayoutRoot"
          Background="Transparent">
 
        <Grid.RowDefinitions>
            <RowDefinition Height="3*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="6*" />
        </Grid.RowDefinitions>
 
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="24,24,0,12">
            <TextBlock x:Name="ApplicationTitle"
                       Text="{Binding ApplicationTitle}"
                       Margin="5,5,0,0"/>
            <TextBlock x:Name="PageTitle"
                       Text="{Binding PageName}"
                       Margin="5,10,0,0" />
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentGrid"
              Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
 
            <!--Page2Button-->
            <Button x:Name="Page2Button"
                    Content="Page 2"
                    Margin="0"
                    Grid.Column="0"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page2ButtonClicked"
                                                                   Command="{Binding Page2Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>
 
            <!--Page3Button-->
            <Button x:Name="Page3Button"
                    Content="Page 3"
                    Margin="0"
                    Grid.Column="1"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page3ButtonClicked"
                                                                   Command="{Binding Page3Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>
 
            <!--Page4Button-->
            <Button x:Name="Page4Button"
                    Content="Page 4"
                    Margin="0"
                    Grid.Column="2"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page4ButtonClicked"
                                                                   Command="{Binding Page4Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>
        </Grid>
 
        <ContentControl Grid.Row="2"
                        Height="204"
                        HorizontalAlignment="Left"
                        Name="contentControl1"
                        VerticalAlignment="Top"
                        Width="466" />
 
    </Grid>
   <!--LayoutRoot contains the root grid where all other page content is placed-->
    <Grid x:Name="LayoutRoot"
          Background="Transparent">

        <Grid.RowDefinitions>
            <RowDefinition Height="3*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="6*" />
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="24,24,0,12">
            <TextBlock x:Name="ApplicationTitle"
                       Text="{Binding ApplicationTitle}"
                       Margin="5,5,0,0"/>
            <TextBlock x:Name="PageTitle"
                       Text="{Binding PageName}"
                       Margin="5,10,0,0" />
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentGrid"
              Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <!--Page2Button-->
            <Button x:Name="Page2Button"
                    Content="Page 2"
                    Margin="0"
                    Grid.Column="0"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page2ButtonClicked"
                                                                   Command="{Binding Page2Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>

            <!--Page3Button-->
            <Button x:Name="Page3Button"
                    Content="Page 3"
                    Margin="0"
                    Grid.Column="1"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page3ButtonClicked"
                                                                   Command="{Binding Page3Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>

            <!--Page4Button-->
            <Button x:Name="Page4Button"
                    Content="Page 4"
                    Margin="0"
                    Grid.Column="2"
                    d:LayoutOverrides="Width, Height"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                <Custom:Interaction.Triggers>
                    <Custom:EventTrigger EventName="Click">
                        <GalaSoft_MvvmLight_Command:EventToCommand x:Name="Page4ButtonClicked"
                                                                   Command="{Binding Page4Command, Mode=OneWay}" />
                    </Custom:EventTrigger>
                </Custom:Interaction.Triggers>
            </Button>
        </Grid>

        <ContentControl Grid.Row="2"
                        Height="204"
                        HorizontalAlignment="Left"
                        Name="contentControl1"
                        VerticalAlignment="Top"
                        Width="466" />

    </Grid>

As a reminder, to add the Grid Columns, one way was to navigate to the grid.row 1 where the Columns were desired, and then goto the properties window.  Find ColumnDefinitions and click, it will open the Collection Editor: ColumnDefinitions.   Simply Add Columns.

Modify MainPageViewModel.cs

RelayCommands:  The Page3 RelayCommands already exists First, we’ll add the RelayCommand for Page4.  We’ll come back to the Page3 RelayCommand because it already exists in the viewmodel for Page3.

1
2
3
4
5
        public RelayCommand Page4Command
        {
            get;
            private set;
        }
        public RelayCommand Page4Command
        {
            get;
            private set;
        }
1
2
3
4
5
6
7
8
       private object GoToPage4()
        {
            //hmmm, will msg be an issue or conflict??  
            var msg = new GoToPageMessage() { PageName = "Page4View" };
            Messenger.Default.Send<GoToPageMessage>(msg);
 
            return null;
        }
       private object GoToPage4()
        {
            //hmmm, will msg be an issue or conflict??  
            var msg = new GoToPageMessage() { PageName = "Page4View" };
            Messenger.Default.Send<GoToPageMessage>(msg);

            return null;
        }

In the constructor public MainPageViewModel(), insert:

1
Page4Command = new RelayCommand(() => GoToPage4());
Page4Command = new RelayCommand(() => GoToPage4());

Modify MainPageView.xaml.cs

We’re getting closer, but it still does not work.  Why??  We are not switching the views in the content control and that code is located in the MainPageView.xaml.cs (code behind).  Open MainPageView.xaml.cs and add the reference:

1
using MvvmControlChange.Content.Page4.View;
using MvvmControlChange.Content.Page4.View;

Add the property to change to the Page4View:

1
2
3
4
5
6
7
8
9
10
        private Page4View _page4View;
        private Page4View Page4View
        {
            get
            {
                if (_page4View == null)
                    _page4View = new Page4View();
                return _page4View;
            }
        }
        private Page4View _page4View;
        private Page4View Page4View
        {
            get
            {
                if (_page4View == null)
                    _page4View = new Page4View();
                return _page4View;
            }
        }

Update the switch statement to include the Page4View.  The updated code should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
            switch (action.PageName)
            {
                case "Page2View":
                    if (this.contentControl1.Content != this.Page2View)
                        this.contentControl1.Content = this.Page2View;
                    break;
                case "Page3View":
                    if (this.contentControl1.Content != this.Page3View)
                        this.contentControl1.Content = this.Page3View;
                    break;
                case "Page4View":
                    if (this.contentControl1.Content != this.Page4View)
                        this.contentControl1.Content = this.Page4View;
                    break;
                default:
                    break;
            }
            switch (action.PageName)
            {
                case "Page2View":
                    if (this.contentControl1.Content != this.Page2View)
                        this.contentControl1.Content = this.Page2View;
                    break;
                case "Page3View":
                    if (this.contentControl1.Content != this.Page3View)
                        this.contentControl1.Content = this.Page3View;
                    break;
                case "Page4View":
                    if (this.contentControl1.Content != this.Page4View)
                        this.contentControl1.Content = this.Page4View;
                    break;
                default:
                    break;
            }

RUN the program – With the exception of the Page3 Button on MainPageView –> IT WORKS!!!  

I can change between Page2 & Page4 from MainPageView and when Page2View is displayed in the user control, I can swtich to Page3.

Page3 Button

Simply add the appropriate code to the  MainPageViewModel.cs to make the MainPageView Page3 button work correctly.  Here is the code I added, you’ll have to weave it in the correct location:

1
2
3
4
5
6
7
8
 private object GoToPage3()
        {
            var msg = new GoToPageMessage() { PageName = "Page3View" };
            Messenger.Default.Send<GoToPageMessage>(msg);
 
            //System.Windows.MessageBox.Show("Navigate to Page 2!");
            return null;
        }
 private object GoToPage3()
        {
            var msg = new GoToPageMessage() { PageName = "Page3View" };
            Messenger.Default.Send<GoToPageMessage>(msg);

            //System.Windows.MessageBox.Show("Navigate to Page 2!");
            return null;
        }
1
2
3
4
5
 public RelayCommand Page3Command
        {
            get;
            private set;
        }
 public RelayCommand Page3Command
        {
            get;
            private set;
        }
1
Page3Command = new RelayCommand(() => GoToPage3());
Page3Command = new RelayCommand(() => GoToPage3());

 

The completed tutorial can be found HERE.

END of POST C# MVVMLight tutorial.

4 comments to C# MVVMLight Tutorial 2 – Adding a Page 4 View

  • Edwin Nuguid

    Your C# tutorial is aids my C# education. ‘Sincerely appreciate the work you put in.

  • Ian

    I found this very useful.

  • admin

    This is Mark, the author. I need to add a note that we’ve since learned more from this post almost a year ago. For one thing, we put all our view and viewmodels into one folder. Otherwise, you have add using statements to more folders in some places of the code and its just easier to have them all dumped into one folder. Maybe there’s another way around on that, but that’s what we found. Good luck and clean coding.

    Oh, another quick note, get ReSharper!! I don’t work for them, but it’s worth it…

  • Alan

    Thanks for putting the tutorial together. Maybe you can do a mini-update as to how you go from here to the new folder arrangement and which statements you need to change where to get it all working again 🙂 Thanks.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" extra="">