Click here to Skip to main content
15,867,308 members
Articles / MVVM

Using Microsoft Expression Blend 4.0 to Implement MVVM

Rate me:
Please Sign up or sign in to vote.
4.33/5 (3 votes)
1 Jun 2011CPOL3 min read 48.9K   809   14   5
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:

XML
<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:

    XML
     <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:
    XML
    <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:
    C#
    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:

    XML
     <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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Philippines Philippines
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Unareshraju15-Feb-12 0:35
Unareshraju15-Feb-12 0:35 
Generalnice work Pin
Princess Grace Dimaculangan5-Jun-11 17:50
Princess Grace Dimaculangan5-Jun-11 17:50 
GeneralNice work, but copy & paste errors Pin
Nic_Roche1-Jun-11 12:12
professionalNic_Roche1-Jun-11 12:12 
GeneralRe: Nice work, but copy & paste errors Pin
Gildon Opao1-Jun-11 17:05
Gildon Opao1-Jun-11 17:05 
GeneralRe: Nice work, but copy & paste errors Pin
Richard Deeming8-Jun-11 5:21
mveRichard Deeming8-Jun-11 5:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.