Random programming things I'd want to remember

Wednesday, January 2, 2013

Simple MVVM tutorial for Windows Phone explained

Once I started learning MVVM pattern for Windows Phone, I decided to write a short sample demonstrating all the features so that I can sum it up for myself. Here it is. This article was a great starting point to understand MVVM. The most important thought I got out of that article was the INotifyProperty changed interface, which allows the controls on the phone be notified about the changes in the elements that they are bound to. We'll start with the view:
<!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.5*"/>
                <ColumnDefinition Width="0.5*"/>
            </Grid.ColumnDefinitions>

            <Button Content="{Binding OneText}" Command="{Binding OneCommand}" IsEnabled="{Binding IsButton1Enabled}" />
            <Button Content="{Binding TwoText}" Command="{Binding TwoCommand}" IsEnabled="{Binding IsButton2Enabled}" Grid.Column="1" />
        </Grid>


There are many ways to hook up ViewModel to the View. This is the simplest one:
        public MainPage()
        {
            InitializeComponent();
            this.DataContext = new OneViewModel();
        }


The idea behind the app is very simple: there are two buttons, pressing one of them deactivates it and activates the other. It also changes the Content property of the other button. So my ViewModel should expose two properties for buttons' contents, two commands, and two properties for buttons' enabled/disabled status. Let's get to it. By the way, Random class will be my model, it will provide Content for buttons.
//ViewModel
    public class OneViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        Random r;

        private string _oneText;
        public string OneText
        {
            get { return this._oneText; }
            set
            {
                if (value == this._oneText)
                    return;
                this._oneText = value;
                this.OnPropertyChanged("OneText");
            }
        }

        private string _twoText;
        public string TwoText
        {
            get { return this._twoText; }
            set
            {
                if (value == this._twoText)
                    return;
                this._twoText = value;
                this.OnPropertyChanged("TwoText");
            }
        }

        private bool _isButton1Enabled;
        public bool IsButton1Enabled
        {
            get { return _isButton1Enabled; }
            set
            {
                if (_isButton1Enabled == value)
                    return;
                _isButton1Enabled = value;
                this.OnPropertyChanged("IsButton1Enabled");
            }
        }


        private bool _isButton2Enabled;
        public bool IsButton2Enabled
        {
            get { return _isButton2Enabled; }
            set
            {
                if (_isButton2Enabled == value)
                    return;
                _isButton2Enabled = value;
                this.OnPropertyChanged("IsButton2Enabled");
            }
        }

        private DelegateCommand _oneCommand;
        private DelegateCommand _twoCommand;
        
        //Constructor
        public OneViewModel()
        {
            r = new Random(); //This is my model
            IsButton1Enabled = true;
            IsButton2Enabled = false;
            OneText = r.Next(100).ToString();
            TwoText = r.Next(100).ToString();
            _oneCommand = new DelegateCommand(this.OneCommandAction);
            _twoCommand = new DelegateCommand(this.TwoCommandAction);
        }

        private void TwoCommandAction(object obj)
        {
            OneText = r.Next(100).ToString();
            IsButton1Enabled = true;
            IsButton2Enabled = false;
        }

        private void OneCommandAction(object obj)
        {
            TwoText = r.Next(100).ToString();
            IsButton1Enabled = false;
            IsButton2Enabled = true;
        }

        public ICommand OneCommand { get { return _oneCommand; } }
        public ICommand TwoCommand { get { return _twoCommand; } }

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



Not much to explain here, everything seems pretty obvious and simple. As I said before, the this.OnPropertyChanged(...) methods letting the View know that things change and the View adjusts accordingly.

Here is the definition for the DelegateCommand class.


    public class DelegateCommand : ICommand
    {
        Func<object, bool> canExecute;
        Action<object> executeAction;

        public DelegateCommand(Action<object> executeAction)
            : this(executeAction, null)        {        }

        public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
        {
            if (executeAction == null)
            {
                throw new ArgumentNullException("executeAction");
            }
            this.executeAction = executeAction;
            this.canExecute = canExecute;
        }

        public bool CanExecute(object parameter) //can command execute in its current status?
        {
            bool result = true;
            Func<object, bool> canExecuteHandler = this.canExecute;
            if (canExecuteHandler != null)
            {
                result = canExecuteHandler(parameter);
            }
            return result;
        }

        public event EventHandler CanExecuteChanged; //occurs when changes occur that affect whether or not the command should execute

        public void RaiseCanExecuteChanged()
        {
            EventHandler handler = this.CanExecuteChanged;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        public void Execute(object parameter) //Method to call when the command is invoked
        {
            this.executeAction(parameter);
        }
    }

        


A couple of points of interest. If your buttons don't seem like they are reacting to the commands and you think everything is hooked up properly, check to make sure your ViewModel class is declared as public.

This site has videos on MVVM implementation for more serious things like, navigation and so on.

Also, be sure to check out my other article on MVVM where I implement a simple keyboard on Windows Phone. That tutorial is easier than this one.

For another tutorial, be sure to check my other article on the subject.