Tuesday, December 14, 2010

.NET 3.5 WPF Actions - ConditionalPropertyChangeAction

In my last blog post, .NET 3.5 WPF Triggers - KeyDownTrigger , i spoke of my frustrations regarding the lack of Actions/Behaviors/Triggers in .NET 3.5 Framework versions of WPF and Silverlight (and even 4.0 versions of WPF).

In exploring reproducing some of Actions that exist, my co-worker, who was converting a 4.0 project back to 3.5 (inter-company political reasons really) expressed the need for an Action that did something similar to one he had in Silverlight 4.0.

So i made the ConditionalPropertyChangeAction, here's my code for that below:

using System;
using System.Windows.Interactivity;
using System.Windows;
using System.ComponentModel;

namespace ToP.Common.Actions
{
    [Description("Changes the value of a property on the attached object IF a property on a target object is equal to the given value")]
    public class ConditionalPropertyChangeAction : TargetedTriggerAction<FrameworkElement>
    {
        protected override void Invoke(object parameter)
        {
            var pInfo = Target.GetType().GetProperty(TargetPropertyName);
            var pValue = pInfo.GetValue(Target, null);

            if (Convert.ChangeType(pValue, pInfo.PropertyType, null) == Convert.ChangeType(TargetPropertyValue, pInfo.PropertyType, null)
             || pValue.Equals(TargetPropertyValue))
            {
                var newProperty = AssociatedObject.GetType().GetProperty(Property);
                newProperty.SetValue(AssociatedObject, Convert.ChangeType(Value, newProperty.PropertyType, null), null);
            }
        }

        [Category("Common Properties"), Description("Property Name on the Target to be used with the comparison")]
        public string TargetPropertyName
        {
            get
            {
                return (string)GetValue(PropertyNameProperty);
            }
            set
            {
                SetValue(PropertyNameProperty, value);
            }
        }
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.Register("TargetPropertyName", typeof(string), typeof(ConditionalPropertyChangeAction), new PropertyMetadata(string.Empty));

        [Category("Common Properties"), Description("Property Value on the Target to be used with the comparison")]
        public object TargetPropertyValue
        {
            get
            {
                return GetValue(PropertyValueProperty);
            }
            set
            {
                SetValue(PropertyValueProperty, value);
            }
        }
        public static readonly DependencyProperty PropertyValueProperty =
            DependencyProperty.Register("TargetPropertyValue", typeof(object), typeof(ConditionalPropertyChangeAction), new PropertyMetadata(string.Empty));

        [Category("Conditioned Properties"), Description("Property to change, IF comparison conditions are met")]
        public string Property
        {
            get
            {
                return (string)GetValue(PropertyProperty);
            }
            set
            {
                SetValue(PropertyProperty, value);
            }
        }
        public static readonly DependencyProperty PropertyProperty =
            DependencyProperty.Register("Property", typeof(string), typeof(ConditionalPropertyChangeAction), new PropertyMetadata(string.Empty));
        [Category("Conditioned Properties"), Description("Value to be assigned to the Property, IF comparison conditions are met")]
        public object Value
        {
            get
            {
                return GetValue(ValueProperty);
            }
            set
            {
                SetValue(ValueProperty, value);
            }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(ConditionalPropertyChangeAction), new PropertyMetadata(string.Empty));
    }
}


Here's a simple example of this being used:
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ToP_Common_Actions="clr-namespace:ToP.Common.Actions;assembly=ToP.Common" x:Class="ToP.Test.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
     <Grid.RowDefinitions>
      <RowDefinition Height="20" />
   <RowDefinition Height="20" />
   <RowDefinition />   
  </Grid.RowDefinitions>
     <TextBox Grid.Row="0" x:Name="textBox" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Center" Width="196"/>
  <Button Grid.Row="1" x:Name="button" Content="Button" VerticalAlignment="Top" HorizontalAlignment="Center">
      <i:Interaction.Triggers>
       <i:EventTrigger EventName="Click">
        <ToP_Common_Actions:ConditionalPropertyChangeAction TargetName="textBox" TargetPropertyName="Text" Property="Content" Value="SUCCESS!" TargetPropertyValue="TEST"/>
       </i:EventTrigger>
      </i:Interaction.Triggers>
     </Button>        
    </Grid>
</Window>

In the above example, i have attached the Action to the Click EventTrigger. I set it up so that when triggered, if the TextProperty is equal to "TEST", then it will change the value of the ContentProperty of the object the action is attached to (which is the Button itself in this example).

Pretty simple, right?

You can customize the Action further to suit your needs, and make it more intuitive. If you do, i would love to see what you do with it!

There will be more Actions\Triggers\Behaviors to come...Stay tuned!