Continuing to Brush up in WPF/MVVM, and because I have been doing WP7 and WP8 for the last 2 months or so, today I decided to write some sample code implementing MVVM on a simple Windows Phone app.

I am trying to keep it simple, so no RelayCommand, no factory abstraction, etc, etc... But this makes it pretty simple to understand how the moving pieces fit together in MVVM, while touching a bit of Commanding. Here it goes:

First the Page that will contain it all:

 

<phone:PhoneApplicationPage 
    x:Class="Memo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="PageTitle" Text="Memo" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

So far its just what VS12 created on its own. Below I added the view to the ContentPanel Children collection. I don't really like this, but just so I don't have to spend time trying to figure out how to make a ContentPresenter take all available space. And no, using Stretch won't work.

using Microsoft.Phone.Controls;
using Memo.Views;

namespace Memo
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();
            ContentPanel.Children.Add(new MemoView());
        }
    }
}

 

Now to the View. MVVM, as we all know means MODEL-VIEW-VIEW MODEL. Below is the VIEW part of the pattern. For  this to be more agnostic, we would have a framework injecting the ViewModel in the View DataContext. Or we could have done that on App, or Page. But for this example, this is simple enough:

 

 

<UserControl x:Class="Memo.Views.MemoView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             FontFamily="{StaticResource PhoneFontFamilyNormal}"
             FontSize="{StaticResource PhoneFontSizeNormal}"
             Foreground="{StaticResource PhoneForegroundBrush}"
             HorizontalAlignment="Stretch"
             VerticalAlignment="Stretch"
             d:DesignHeight="480"
             d:DesignWidth="480">

    <Grid x:Name="LayoutRoot"
          Background="{StaticResource PhoneChromeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBox x:Name="txtMemoText"
                 Grid.Row="0"
                 Text="{Binding Mode=TwoWay, Path=MemoText}"
                 TextWrapping="Wrap" />
        <Button Command="{Binding SaveCommand}"
                Grid.Row="1"
                CommandParameter="{Binding}">Save</Button>
    </Grid>
</UserControl>

 

and the code behind does nothing but set the DataContext. This could easily be done in XAML as well.

 

using System.Windows.Controls;
using Memo.ViewModels;

namespace Memo.Views
{
    public partial class MemoView : UserControl
    {
        public MemoView()
        {
            InitializeComponent();
            DataContext = new MemoViewModel();
        }
    }
}

Above we set the View's DataContext to the ViewModel. Here is a simple view model that works well for this exercise:

  

 

using System.ComponentModel;

namespace Memo.ViewModels
{
    public class MemoViewModel : INotifyPropertyChanged
    {


        private string memoText;
        public string MemoText
        {
            get
            {
                return memoText;
            }
            set
            {
                if (memoText != value)
                {
                    memoText = value;
                    OnPropertyChanged("MemoText");
                }

                // If text is empty cannot execute save command
                if (saveCommand != null)
                {
                    if (memoText != null && memoText.Length > 0)
                        saveCommand.Enabled = true;
                    else
                        saveCommand.Enabled = false;
                }
            }
        }

        private Command saveCommand;
        public Command SaveCommand
        {
            get
            {
                if (saveCommand == null)
                    saveCommand = new Command(SaveMemo);
                return saveCommand;
            }
            set
            {
                saveCommand = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }

        private void SaveMemo()
        {
            // Does stuff here to save the memo to memory;
        }
    }
}

 

I used a custom Command classs that implements ICommand. I will love to hear the comments on the Enabled property. Smiley This works, but even keeping simplicity, I feel it could be better:

 

using System;
using System.Windows.Input;

namespace Memo
{
    public class Command : ICommand
    {

        private readonly Action executeAction;

        private bool enabled;
        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                if (enabled != value)
                {
                    enabled = value;

                    if (CanExecuteChanged != null)
                        CanExecuteChanged(this, new EventArgs());
                }
            }
        }

        public Command(Action executeAction)
        {
            this.executeAction = executeAction;
        }

        public bool CanExecute(object parameter)
        {
            return enabled;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            executeAction();
        }
    }
}

And finally a simple Model. I should have passed the Command all the way over here and the model executes, for better separation of concerns, but really want to keep it simple. Still I believe very useful for those trying to understand the MVVM pattern.

 

using System.ComponentModel;

namespace Memo.Models
{
    class MemoModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }

        private string memoText;
        public string MemoText
        {
            get
            {
                return memoText;
            }
            set
            {
                if (memoText != value)
                {
                    memoText = value;
                    OnPropertyChanged("MemoText");
                }
            }
        }
    }
}

There it is. It worked on my box. Let me know your thoughts.

Thanks!

Mauricio Feijo