Windows Tile Updater (Part 4 – rationalise the code)

From Part 3 of this tutorial, we finished with a small program running on both Windows 8.1 and Phone 8. It shared most of its business logic, but replicated the XAML. It had an ugly even handler to deal with the single action that it performed. In this post, I’m going to attempt to change this to use the MVVM architecture. I’m also going to do this without using a third party framework.

MVVM

If we are to migrate this to an MVVM pattern, then we will need to do the following:
1. Create a view model
2. Bind the view, to the view model
3. Create a model (the business / tile logic that we have is already a model of sorts).

View Model

I usually find it easier to work from the View Model first.

Note:
I’d normally create this as a separate PCL, but it would appear that shared projects are limited in that they cannot reference any other projects. I found no documentation from MS to say why, or even confirm this was the case. This may well be the subject of a future post.

Anyway, we can create the view model in the shared project:

createmodelinsharedproject

Let’s start with the properties:


namespace TileUpdater.ViewModel
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private static MainViewModel _mvm;
        public static MainViewModel MVM
        {
            get
            {
                if (_mvm == null)
                    _mvm = new MainViewModel();

                return _mvm;
            }
        }

        private string _imagePath = @"c:locationimage.png";
        public string ImagePath
        {
            get { return _imagePath; }
            set
            {
                SetProperty<string>(ref _imagePath, value);
            }
        }

        private string _displayText;
        public string DisplayText
        {
            get { return _displayText; }
            set
            {
                SetProperty<string>(ref _displayText, value);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (Equals(storage, value))
            {
                return false;
            }

            storage = value;
            OnPropertyChanged<T>(propertyName);
            return true;
        }

        private void OnPropertyChanged<T>([CallerMemberName]string caller = null)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(caller));
            }
        }
    }
}

And bind them to the Windows 8 view:

<Page
    x:Class="TileUpdater.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TileUpdater"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"    
    DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">
       
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
            <TextBlock FontSize="30" Margin="10">Image</TextBlock>            
            <TextBox Text="{Binding ImagePath}" Margin="10"/>
        </StackPanel>
        
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="My Phone Number: 123456 890 12" Margin="10"/>
        </StackPanel>

        <Button Grid.Row="2" Click="Button_Click">Update</Button>
    </Grid>
</Page>

Quick explanation

I simply created a singleton instance of the view model. Did I have to create a singleton? No – there’s a hundred different ways I could have done this; if I had several view models then you could create a small dictionary with them in (in the style of MVVMLight). I specifically didn’t want to use a framework here, and I didn’t want to overcomplicate – say what you like about singletons, but they are simple.

Now let’s bind the phone

Take the XAML in the Windows 8 XAML and paste it into MainPage.xaml:

…
DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
            <TextBlock FontSize="30" Margin="10">Image</TextBlock>            
            <TextBox Text="{Binding ImagePath}" Margin="10"/>
        </StackPanel>
        
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="My Phone Number: 123456 890 12" Margin="10"/>
        </StackPanel>

        <Button Grid.Row="2" Click="Button_Click">Update</Button>
    </Grid>

Command Binding

The next step is to bind the commands. MVVM Light allows RelayCommand for this purpose, but let’s see what we can do on our own.

The first step is to bind the command. For now, let’s just do this in the Windows 8 MainPage.xaml:

        <!--<Button Grid.Row="2" Click="Button_Click">Update</Button>-->
        <Button Grid.Row="2" Command="{Binding UpdateCommand}">Update</Button>

We’ve now bound the command, but have nothing to bind. Without a framework, this is very much a matter of rolling your own. First, you need to add the command:

addupdatecommand

The implementation is quite simple:

namespace TileUpdater.ViewModel
{
    class UpdateCommand : ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            TileUpdater.UpdateTile.UpdateTileImage();
        }
    }
}

Finally, just link this up the the ViewModel (TileUpdater.Shared):

        private ICommand _updateCommand;
        public ICommand UpdateCommand
        {
            get
            {
                if (_updateCommand == null)
                {
                    _updateCommand = new UpdateCommand();
                }
                return _updateCommand;
            }
            set { _updateCommand = value; }
        }

Needless to say that we need to make the same change in the phone app. The XAML for MainPage should be exactly the same:

        <!--<Button Grid.Row="2" Click="Button_Click">Update</Button>-->
        <Button Grid.Row="2" Command="{Binding UpdateCommand}">Update</Button>
    </Grid>

What about the Model – isn’t this just VVM?

Okay – so, we’re now basically using the MVVM pattern. We don’t technically have a Model, though. Actually, TileUpdater is really the Model, so let’s simply call it one:

justcallitamodel

Conclusion

Now we’re running an MVVM pattern and sharing most of the code. However, there’s a lot of duplicate XAML, and the UI is a bit scrappy, so We’ll have a look at that in the next post.

Leave a Reply

Your email address will not be published. Required fields are marked *