Using Microsoft Expression Blend 4.0 to Implement MVVM






4.33/5 (3 votes)
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:
- Handling Events in Silverlight controls such as
Loaded
,MouseRightButtonDown
,KeyDown
, etc. - Handling Events and Properties inside a ChildWindow or
Popup
. - 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.
- InvokeCommandAction. The
InvokeCommandAction
action specifies the target object that contains the command that you want to invoke. (source: Microsoft) - CallMethodAction. You can use the
CallMethodAction
action to call a method that is defined for a specified object. The method being called must be apublic
method that takes no arguments and does not return a value or apublic
method whose signature matches that of an event handler. (source: Microsoft) - 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
- Create a New Silverlight Application + Website in Microsoft Blend named
MVVMWithBlend
. - 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 selectChildWindow
. Type ChildWindow1.xaml on the name and click Ok. This will create aChildWindow
control. - Go back to MainPage.xaml and add the following controls as shown below:
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
- Click on Assets > Behaviors and add the InvokeCommandAction to the controls as shown below:
- Set the following
InvokeCommandAction
Properties onGrid
:- Command Property = "
TextInputUpdateCommand
" - EventName = "
MouseRightButtonDown
"
- Command Property = "
- Set the following
InvokeCommandAction
Properties onTextBox
:- Command Property = "
TextInputUpdateCommand
" - EventName = "
KeyDown
"
- Command Property = "
- Set the following
InvokeCommandAction
properties onEllipse
:- Command Property = "
LoadedCommand
" - EventName = "
Loaded
"
- Command Property = "
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>
- Set the following
- 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>
- 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; }
- Click on Assets > Behaviors and add the CallMethodAction and ChangePropertyAction to the OK and Cancel buttons as shown below:
- Set the following
CallMethodAction
Properties inCancelButton
:EventName
= "Click
"TargetObject
= "System.Windows.Controls.ChildWindow
" (Use the Artboard element picker and click on the Child Window)MethodName
= "Close
"
- Set the following
ChangePropertyAction
properties inCancelButton
:EventName
= "Click
"TargetObject
= "System.Windows.Controls.ChildWindow
" (Use theArtboard
element picker and click on the Child Window)PropertyName
= "DialogResult
"Value
=checkmark
- Set the following
CallMethodAction
properties inOKButton
:EventName
= "Click
"TargetObject
= "System.Windows.Controls.ChildWindow
" (Use theArtboard
element picker and click on the Child Window)MethodName
= "Close
"
- Set the following
ChangePropertyAction
properties inOKButton
:EventName
= "Click
"TargetObject
= "System.Windows.Controls.ChildWindow
" (Use theArtboard
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>
- Set the following
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