Tech Off Thread

2 posts

A very simple implementation sample of MVVM for the Windows Phone

Back to Forum: Tech Off
  • Mauricio Feijo

    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

  • wkempf

    Not bad. Obviously this is over simplified and doesn't really even begin to touch on any of the pain points of MVVM and their solutions, but for someone new to the pattern it shows the various parts. The only real complaint on that front is that while you created a MemoModel to illustrate the Model in MVVM, you didn't use it anywhere. For someone new to the pattern, there's absolutely no explanation/illustration of why there are three components involved in this pattern.

    Other critiques:

    1. I don't like the Enabled property on your command. This externalizes something that shouldn't be externalized. This means anyone can turn the command on and off, and such code is very likely to lead to buggy implementations.
    2. I'm confused about why MainPage is used as a shell for the View and doesn't itself follow the MVVM pattern?
    3. In MemoViewModel.MemoText the code that enables/disables the SaveCommand (see point 1) should have been placed inside the first if statement. There's no reason to update the command if the property wasn't updated.
    4. It's safer and more efficient to initialize the SaveCommand in the constructor rather than lazily initializing it in the property getter.
    5. Minor point, but OnPropertyChanged isn't thread safe. Proper implementation of code that raises an event first assigns the event to a temporary delegate and then uses the temporary when checking for null and raising the event. In this case it might not be necessary, because technically ViewModels should have the same thread affinity as the View (i.e. it should behave the same way as, or possibly even inherit from, DispatcherObject). Therefore OnPropertyChanged should only ever be called from a single thread. Still, implementations like you showed always pop out at me as being incorrect.

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.