Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Microsoft Expression Blend 4.0 to Implement MVVM

0.00/5 (No votes)
1 Jun 2011 1  
How to use Microsoft Expression Blend 4.0 Behaviors to help in the implemention of MVVM

Introduction

Implementing MVVM strictly in Silverlight has many pain points. Some of these are:

  1. Handling Events in Silverlight controls such as Loaded, MouseRightButtonDown, KeyDown, etc.
  2. Handling Events and Properties inside a ChildWindow or Popup.
  3. Closing a ChildWindow.

This project was created in order to make use of the following Microsoft Blend 4.0 Behaviors to ease the many pain points of implementing MVVM using Silverlight.

  1. InvokeCommandAction. The InvokeCommandAction action specifies the target object that contains the command that you want to invoke. (source: Microsoft)
  2. CallMethodAction. You can use the CallMethodAction action to call a method that is defined for a specified object. The method being called must be a public method that takes no arguments and does not return a value or a public method whose signature matches that of an event handler. (source: Microsoft)
  3. ChangePropertyAction. You can use the ChangePropertyAction behavior to easily either change or increment the property of an object and then, optionally, define a transition. By default, the transition is instantaneous.

Setting up the Forms

  1. Create a New Silverlight Application + Website in Microsoft Blend named MVVMWithBlend.
  2. Add a new ChildWindow named childWindow1.xaml. Click on the Projects tab and right-click on the project file named MVVMWithBlend. Click on Add New Item... and select ChildWindow. Type ChildWindow1.xaml on the name and click Ok. This will create a ChildWindow control.

    BlendMVVM003.PNG

  3. Go back to MainPage.xaml and add the following controls as shown below:

    BlendMVVM001.PNG

NOTE: You can paste the following code in Bold below for convenience:

<Grid x:Name="LayoutRoot" Background="White">
  <Grid Height="30" Margin="128,139,203,0" VerticalAlignment="Top">
     <Rectangle Fill="#FFF4F4F5" Stroke="Black"/>
     <TextBlock HorizontalAlignment="Left" Margin="98,4,0,3" 
	x:Name="textBlock1" Text="Right Click on Me" />
  </Grid>
  <TextBox Height="36" Margin="128,0,203,193" TextWrapping="Wrap" 
	Text="TextBox" VerticalAlignment="Bottom"/>
  <Ellipse Fill="#FFF4F4F5" Height="31" Margin="149,199,278,0" 
	Stroke="Black" VerticalAlignment="Top"/>
  <Button Content="Open Child Window" Height="23" 
	HorizontalAlignment="Left" Margin="128,92,0,0" x:Name="button1" 
	VerticalAlignment="Top" Width="147" />
</Grid>        

Setting up the Behaviors

  1. Click on Assets > Behaviors and add the InvokeCommandAction to the controls as shown below:

    BlendMVVM005.PNG

    1. Set the following InvokeCommandAction Properties on Grid:
      • Command Property = "TextInputUpdateCommand"
      • EventName = "MouseRightButtonDown"
    2. Set the following InvokeCommandAction Properties on TextBox:
      • Command Property = "TextInputUpdateCommand"
      • EventName = "KeyDown"
    3. Set the following InvokeCommandAction properties on Ellipse:
      • Command Property = "LoadedCommand"
      • EventName = "Loaded"

    NOTE: You can paste the following code in Bold below for convenience:

     <Grid x:Name="LayoutRoot" Background="White">
      <Grid Height="30" Margin="128,139,203,0" VerticalAlignment="Top">
       <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseRightButtonDown">
         <i:InvokeCommandAction Command="{Binding TextInputUpdateCommand}"/>
        </i:EventTrigger>
       </i:Interaction.Triggers>
       <Rectangle Fill="#FFF4F4F5" Stroke="Black"/>
       <TextBlock HorizontalAlignment="Left" Margin="98,4,0,3" 
    	x:Name="textBlock1" Text="Right Click on Me" />
      </Grid>
      <TextBox Height="36" Margin="128,0,203,193" 
    	TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Bottom">
       <i:Interaction.Triggers>
        <i:EventTrigger EventName="KeyDown">
         <i:InvokeCommandAction Command="{Binding TextInputUpdateCommand}"/>
        </i:EventTrigger>
       </i:Interaction.Triggers>
      </TextBox>
      <Ellipse Fill="#FFF4F4F5" Height="31" Margin="149,199,278,0" 
    	Stroke="Black" VerticalAlignment="Top">
       <i:Interaction.Triggers>
        <i:EventTrigger>
         <i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
        </i:EventTrigger>
       </i:Interaction.Triggers>
      </Ellipse>
      <Button Content="Open Child Window" Command="{Binding PopupVM}" 
    	Height="23" HorizontalAlignment="Left" Margin="128,92,0,0" 
    	x:Name="button1" VerticalAlignment="Top" Width="147" />
    </Grid>
  2. Open the ChildWindow1.xaml and replace the following code in Bold with the one shown below:
    <controls:ChildWindow
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:controls="clr-namespace:System.Windows.Controls;
    		assembly=System.Windows.Controls"
               xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity 
    	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    	mc:Ignorable="d" x:Name="childWindow" x:Class="MVVMWithBlend.ChildWindow1"
               Width="400" Height="296" 
               Title="ChildWindow1">
        <Grid x:Name="LayoutRoot" Margin="2,0,2,-35">
            <Grid.RowDefinitions>
             <RowDefinition />
             <RowDefinition Height="Auto" MinHeight="31" />
            </Grid.RowDefinitions>
         <Button x:Name="CancelButton" Content="Cancel" Width="75" 
    	Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" 
    	d:LayoutOverrides="Height" Margin="0,0,0,4" />
         <Button x:Name="OKButton" Content="OK" Width="75" Height="23" 
    	HorizontalAlignment="Left" Margin="224,0,0,4" VerticalAlignment="Bottom" 
    	d:LayoutOverrides="Height"/>
        </Grid>
    </controls:ChildWindow>        
  3. Remove the folllowing code behind in ChildWindow1.xaml.cs:
    private void OKButton_Click(object sender, RoutedEventArgs e)
    {
        this.DialogResult = true;
    }
    
    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        this.DialogResult = false;
    }
  4. Click on Assets > Behaviors and add the CallMethodAction and ChangePropertyAction to the OK and Cancel buttons as shown below:

    BlendMVVM004.PNG

    1. Set the following CallMethodAction Properties in CancelButton:
      • EventName = "Click"
      • TargetObject = "System.Windows.Controls.ChildWindow" (Use the Artboard element picker and click on the Child Window)
      • MethodName = "Close"
    2. Set the following ChangePropertyAction properties in CancelButton:
      • EventName = "Click"
      • TargetObject = "System.Windows.Controls.ChildWindow" (Use the Artboard element picker and click on the Child Window)
      • PropertyName = "DialogResult"
      • Value = checkmark
    3. Set the following CallMethodAction properties in OKButton:
      • EventName = "Click"
      • TargetObject = "System.Windows.Controls.ChildWindow" (Use the Artboard element picker and click on the Child Window)
      • MethodName = "Close"
    4. Set the following ChangePropertyAction properties in OKButton:
      • EventName = "Click"
      • TargetObject = "System.Windows.Controls.ChildWindow" (Use the Artboard element picker and click on the Child Window)
      • PropertyName = "DialogResult"
      • Value = checkmark

    NOTE: You can also copy and paste the following code for convenience:

     <controls:ChildWindow
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:controls="clr-namespace:System.Windows.Controls;
    		assembly=System.Windows.Controls"
               xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
    	x:Name="childWindow" x:Class="MVVMWithBlend.ChildWindow1"
               Width="400" Height="300" 
               Title="ChildWindow1">
        <Grid x:Name="LayoutRoot" Margin="2">
            <Grid.RowDefinitions>
             <RowDefinition />
             <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Button x:Name="CancelButton" Command="{Binding CancelCommand}" 
    	Content="Cancel" Width="75" Height="23" HorizontalAlignment="Right" 
    	Margin="0,12,0,0" Grid.Row="1" >
             <i:Interaction.Triggers>
              <i:EventTrigger EventName="Click">
               <ei:CallMethodAction MethodName="Close" 
    		TargetObject="{Binding ElementName=childWindow}" />
               <ei:ChangePropertyAction TargetObject="{Binding ElementName=childWindow}" 
    		PropertyName="DialogResult" Value="True"/>
              </i:EventTrigger>
             </i:Interaction.Triggers>
            </Button>
            <Button x:Name="OKButton" Command="{Binding OKCommand}" 
    	Content="OK" Width="75" Height="23" HorizontalAlignment="Left" 
    	Margin="224,12,0,0" Grid.Row="1">
             <i:Interaction.Triggers>
              <i:EventTrigger EventName="Click">
               <ei:CallMethodAction MethodName="Close" 
    		TargetObject="{Binding ElementName=childWindow}" />
               <ei:ChangePropertyAction TargetObject="{Binding ElementName=childWindow}" 
    		PropertyName="DialogResult" Value="True"/>
              </i:EventTrigger>
             </i:Interaction.Triggers>
            </Button>
        </Grid>
    </controls:ChildWindow>

Setting up the ViewModel

Add a new class named ViewModel.cs. Click on the Projects tab and right-click on the project file named MVVMWithBlend. Click on Add New Item... and select Class. Type ViewModel on the name and click Ok.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace MVVMWithBlend
{
    public class ViewModel
    {
        #region ICommands

        public ICommand TextInputUpdateCommand
        {
            get
            {
                return new InvokeTextUpdateCommand();
            }
        }

        /// <summary>
        /// Gets the loaded Event.
        /// </summary>
        /// <value>The loaded command.</value>
        public ICommand LoadedCommand
        {
            get
            {
                return new InvokeLoadedCommand();
            }
        }

        /// <summary>
        /// Gets the popup.
        /// </summary>
        /// <value>The popup VM.</value>
        public ICommand PopupVM
        {
            get
            {
                return new InvokeChildCommand();
            }
        }

        #endregion

        #region Classes

        public class InvokeTextUpdateCommand : ICommand
        {
            public bool CanExecute(object parameter)
            {
                if (parameter != null)
                {
                    CanExecuteChanged.Invoke(parameter, new EventArgs());
                }
                return true;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                MessageBox.Show("Text is Updated");
            }
        }

        public class InvokeLoadedCommand : ICommand
        {
            public bool CanExecute(object parameter)
            {
                if (parameter != null)
                {
                    CanExecuteChanged.Invoke(parameter, new EventArgs());
                }
            return true;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                MessageBox.Show("Loaded Event is Triggered");
            }
        }

        public class InvokeChildCommand : ICommand
        {
            public bool CanExecute(object parameter)
            {
                if (parameter != null)
                {
                    CanExecuteChanged.Invoke(parameter, new EventArgs());
                }
                return true;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                //Push the ViewModel into the Popup
                ChildWindow1 child = new ChildWindow1();
                ViewModelPopup pop = new ViewModelPopup();
                child.DataContext = pop;
                child.Show();
            }
        }

        /// <summary>
        /// View Model of Child Window
        /// </summary>
        public class ViewModelPopup
        {
            public ICommand OKCommand
            {
                get
                {
                    DialogResult = true;
                    return new InvokeOkCommand();
                }
            }

            public ICommand CancelCommand
            {
                get
                {
                    DialogResult = false;
                    return new InvokeCancelCommand();
                }
            }

            public class InvokeOkCommand : ICommand
            {
                public bool CanExecute(object parameter)
                {
                    if (parameter != null)
                    {
                        CanExecuteChanged.Invoke(parameter, new EventArgs());
                    }
                    return true;
                }

                public event EventHandler CanExecuteChanged;

                public void Execute(object parameter)
                {
                    MessageBox.Show("Ok is Clicked");
                }
            }

            public class InvokeCancelCommand : ICommand
            {
                public bool CanExecute(object parameter)
                {
                    if (parameter != null)
                    {
                        CanExecuteChanged.Invoke(parameter, new EventArgs());
                    }
                    return true;
                }

                public event EventHandler CanExecuteChanged;

                public void Execute(object parameter)
                {
                    MessageBox.Show("Cancel is Clicked");
                }
            }
        }
        #endregion
    }
}

History

  • 06/01/2011: Initial draft
  • 06/02/2011: Adjusted lines and corrected copy and paste error

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here