Quantcast
Channel: MVVM – Dean Chalk
Viewing all articles
Browse latest Browse all 26

WPF / MVVM – Property Changed To Command Behavior

$
0
0

Ok, its been a while since my last post, so Ive got a lot of saved-up article ideas to write – so here comes the first (many more to follow).

MVVM – its now ubiquitous in WPF ‘line of business’ application development, and there’s many ways to tweak the behavior of controls to better (or more easily) support MVVM through the usual control extension technologies – mainly ‘Value Converters’, and ‘Attached Behaviors’

This article is a neat trick I’ve used before. Basically its a generic behavior that can fire an ICommand (via an MVVM binding – of course) whenever a dependency property on your control changes its value.

Normally, we would bind a CLR property on our ViewModel directly to our Control’s dependency property (e.g. bind the viewmodel’s Title string property to the Text property of a TextBox – etc). However, there are sometimes scenarios where we want to use an ICommand (with its Execute and CanExecute behavior) rather than a direct binding (or an indirect one via valueconverters perhaps).

To get this to work, we will leverage the DependencyPropertyDescriptor, and its ability to provide us with DependencyProperty changed tracking. We wrap all of this in a Behavior like so:

(NOTE: In order to inherit from Behavior you will need to add a reference to a dll that you will find somewhere like this – C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.0\Libraries\System.Windows.Interactivity.dll)

public class PropertyChangedCommandBehavior : Behavior<DependencyObject>
{
    public static readonly DependencyProperty PropertyChangedCommandProperty =
        DependencyProperty.Register("PropertyChangedCommand", 
            typeof (ICommand), typeof (PropertyChangedCommandBehavior), 
                new PropertyMetadata(null, ApplyChanged));

    public static readonly DependencyProperty DependencyPropertyNameProperty =
        DependencyProperty.Register("DependencyPropertyName", 
            typeof (string), typeof (PropertyChangedCommandBehavior), 
                new PropertyMetadata(string.Empty, ApplyChanged));

    private DependencyPropertyDescriptor descriptor;

    public ICommand PropertyChangedCommand
    {
        get { return (ICommand) GetValue(PropertyChangedCommandProperty); }
        set { SetValue(PropertyChangedCommandProperty, value); }
    }

    public string DependencyPropertyName
    {
        get { return (string) GetValue(DependencyPropertyNameProperty); }
        set { SetValue(DependencyPropertyNameProperty, value); }
    }

    private static void ApplyChanged(DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as PropertyChangedCommandBehavior;
        if (behavior != null)
            behavior.Setup();
    }


    protected override void OnAttached()
    {
        Setup();
    }

    protected override void OnDetaching()
    {
        if (descriptor != null && AssociatedObject != null)
            descriptor.RemoveValueChanged(AssociatedObject, 
                OnPropertyValueChanged);
    }

    private void Setup()
    {
        if (descriptor != null || string.IsNullOrEmpty(DependencyPropertyName) 
            || AssociatedObject == null)
            return;
        descriptor = DependencyPropertyDescriptor.FromName(DependencyPropertyName, 
            AssociatedObject.GetType(), AssociatedObject.GetType());
        if (descriptor == null)
            return;
        descriptor.AddValueChanged(AssociatedObject, OnPropertyValueChanged);
    }

    private void OnPropertyValueChanged(object sender, EventArgs e)
    {
        var value = AssociatedObject.GetValue(descriptor.DependencyProperty);
        if (PropertyChangedCommand == null || 
            !PropertyChangedCommand.CanExecute(value))
            return;
        PropertyChangedCommand.Execute(value);
    }
}

That was our behavior class. You might want to beef it up with some defensive coding and other features – this example code is just a bare-bones that illustrates the idea.

Below is the code to my ViewModel. Again its just a stripped down demo and is missing a lot of stuff you’d normally find in a ViewModel, but it’s all I need to demonstrate this technique.

The Command simply does a Debug.Writeline on the command parameter, which we will wire up to the Text inside a TextBox.

(I have included a very simple version of a DelegateCommand class – again this is very lightweight and missing many of the features you’d expect, but is good enough for this article.

public class MainViewModel
{
    public ICommand TextChangedCommand { get; private set; }

    public MainViewModel()
    {
        TextChangedCommand = new DelegateCommand(v => Debug.WriteLine(v));
    }
}

public class DelegateCommand : ICommand
{
    private Action<object> action;
    public DelegateCommand(Action<object> action)
    {
        this.action = action;
    }
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        action(parameter);
    }

    public event EventHandler CanExecuteChanged;
}

Here is the code-behind of our view, as you’d expect – all we are doing is hooking up the ViewModel

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainViewModel();
}

And finally, here is our view. Notice the XAML for our behavior. You can see that we are going to be channeling changes from our Text property on the TextBox straight to our Command via the DependencyPropertyDescriptor

<Window x:Class="PropertyCommandBehaviorExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:b="clr-namespace:PropertyCommandBehaviorExample"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Text="test">
            <i:Interaction.Behaviors>
                <b:PropertyChangedCommandBehavior DependencyPropertyName="Text" 
                   PropertyChangedCommand="{Binding TextChangedCommand}" />
            </i:Interaction.Behaviors>
        </TextBox>
    </Grid>
</Window>

Now when I run this code and start typing into the TextBox, all the text changes get spat out in my Console.

So, where again would I use this? Well, I’d probably find is useful for adding rules to my CLR Property bindings, so for example if a checkbox isn’t checked, a string property cant be updated. The ‘CanExecute’ Func on the DelegateCommand could be where that rule is expressed.

Do you think thats a good use of this idea ? Or do you have a better example ? Let me know what you think, and thanks for reading this post.

Dean


Viewing all articles
Browse latest Browse all 26

Trending Articles