As mentioned previously, I recently used System.Windows.Interactivity library to make a command respond to an event on WPF controls without using any code-behind. In this post, I would give a brief overview and show some code on how to do it. I will try and keep the post to the point and not write anything about hooking up events with code or anything like that. Usual disclaimer applies – I am not entirely familiar with the internals but I know how to make it work and why it works.
So lets start with my simple requirement. I have a text box and as I enter I want to fire a command which processes the text and displays it on a textblock. Of course you can hook up both the controls to the same property in the ViewModel and with .NET 4.0 you can be sure that the getter will fire again when NotifyPropertyChanged is fired. But that is not the point here.
My XAML would simply have a textbox and a textblock. On textbox.TextChanged event fired, I would like to execute a command in my view model. The XAML is shown below.
<UserControl x:Class="Buddi.Training.Advanced.Interactivity.EventToCommandDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Buddi.Training.Infra"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<Grid Background="Beige">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<TextBox Text="{Binding SampleCommandParam,UpdateSourceTrigger=PropertyChanged}" Margin="20">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<local:EventToCommand Command="{Binding SampleCommand}"
CommandParameter="{Binding SampleCommandParam}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Text="{Binding Message}" Grid.Row="1" Padding="30" FontFamily="Consolas" FontWeight="14"/>
</Grid>
</UserControl>
You need to see how I am hooking up the event to the command in viewmodel which is the DataContext of the View (UserControl to be precise). Lets disect what we have here - We add an event trigger to the TriggersCollection on the Grid using the Interactivity.Triggers attached properties. An event trigger comes with the System.Windows.Interactivity.dll assembly. So add a reference to that library using the "Add Reference" dialog. The Event Trigger then expects an action that can be anything that derives from the TriggerAction<FrameworkElement> class. The TriggerAction That's it! you can now program to the events using the commands that you already have. This lets you keep your code-behind clean and write more testable code than ever. I hope this is useful inspite of it not being the best of the articles. By the way, almost every MVVM framework out there provides an implementation of Event To Command action -eg : Caliburn, Cinch, you name it ... but not always it is possible for us to use a third party framework just for this one reason. In such cases, I thought it is good to know that you can acheive it just by using Microsoft's assembly.
namespace Buddi.Training.Infra
{
public class EventToCommand : TriggerAction<FrameworkElement>
{
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommand), new UIPropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandParameter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventToCommand), new UIPropertyMetadata(null));
protected override void Invoke(object parameter)
{
if (Command == null) return;
if (Command is RoutedCommand)
{
var rc = Command as RoutedCommand;
if (rc.CanExecute(CommandParameter, base.AssociatedObject))
{
rc.Execute(CommandParameter, base.AssociatedObject);
}
}
else
{
if (Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
}
}
}
}
5 comments:
Very interesting blog Krishna. Great to see your passion for software. Keep it up!
oh and your SyntaxHighligher plugin still seems to be broken :)
Good one but how do I pass the event arguments?
Nice one, but how do I pass the
event arguments?
Uday
Great Article,
I could get this to work. I am always getting Command is null and hence nothing happens. Do you the full code sample for download.
TIA
Yaz
Post a Comment