Monday, February 28, 2011

Silverlight 4 TextBox Update Source on Text Changed Behavior

Here is a simple little behavior for updating a TextBox's Text property in Silverlight when the text changes instead of when it loses focus.


Quick and Easy.  Gotta Love Behaviors.

Jacob

Thursday, February 24, 2011

Silverlight 4 DropDropBehavior and FileDragDropBehavior with Command Binding

Here is an easy to use FileDragDropBehavior that you can use in your XAML to implement dragging and dropping of files in your Silverlight 4 Application


Just put the class in your project, reference the namespace in your XAML file and add the behavior to where you want to drop a file.

Jacob

CheckedItemCollection for Silverlight and WPF CheckBox List or RadioButton List

Here is a simple class for wrapping a list of items so they can be checked from a list.  There are some helper methods like CheckAll, UnCheckAll, and CheckWhere that I've found useful in my projects.


Then when you are done and need to get the checked items you can use the handy CheckedItems property.

Jacob

Tuesday, February 22, 2011

ClickCommandTrigger based on MVVMLight EventToCommand TriggerAction

Here is a simple ClickCommandTrigger based on the MVVMLight EventToCommand TriggerAction.  This will make it a little easier to run an MVVMLight command trigger for a simple button click.


Gotta love triggers.

Sunday, February 20, 2011


While working on a recent HTML5 project I found myself wishing for the convenience of data binding.  I was building a timer and was adding a lot of callbacks to update the UI elements on the page.  I figured there was a better way and after a little searching I came across Knockout.

Our Goal



Knockout is a great combination of jQuery templates and data binding that allows for one-way and two-way binding back to a Javascript object.  I've put together an example project with an ugly looking stopwatch, and we'll go through some parts of it in this blog post (just want the code, bro? get it here)

The timer() class with Knockout Observables

/// The timer class.
var timer = function () {
    this.started = new ko.observable(false);
    this.totalSeconds = new ko.observable(0);

    this.seconds = new ko.dependentObservable(function () {
        return (this.totalSeconds() % 60).toFixed(0);
    }, this);

    this.secondsDisplay = new ko.dependentObservable(function () {
        var secs = this.seconds();
        var display = '' + secs;
        if (secs < 10) { display = '0' + secs; }

        // Hack for weird edge case because of setInterval.
        if (display == '010') { display = '10'; }

        return display;
    }, this);

    this.minutes = new ko.dependentObservable(function () {
        return ((this.totalSeconds() / 60) % 60).toFixed(0);
    }, this);

    this.hours = new ko.dependentObservable(function () {
        return (((this.totalSeconds() / 60) / 60) % 60).toFixed(0);
    }, this);

    this.secondHandAngle = new ko.dependentObservable(function () {
        return this.seconds() * 6;
    }, this);

    this.minuteHandAngle = new ko.dependentObservable(function () {
        return this.minutes() * 6;
    }, this);

    this.hourHandAngle = new ko.dependentObservable(function () {
        return this.hours() * 6;
    }, this);

    this.alarm = function () {
        log('alarm fired');
    };
};

// timer.start
timer.prototype.start = function () {
    this.started(true);
    this.startTime = new Date();
    var self = this;

    this.intervalId = setInterval(function () {
        var oldTime = self.startTime;
        self.startTime = new Date();

        var diff = secondsBetween(self.startTime, oldTime);
        var currSeconds = self.totalSeconds();
        self.totalSeconds(currSeconds + diff);
    }, 100);
};

// timer.stop
timer.prototype.stop = function () {
    this.started(false);
    if (this.intervalId) {
        clearInterval(this.intervalId);
    }
};

// helper...
function secondsBetween(date1, date2) {
    return (date1.getTime() - date2.getTime()) / 1000;
};

From the timer class code you can see that we declare our fields as ko.observable()'s.  These are wrappers around our values that help with notifying our bound elements when the values change.  The function() syntax for accessing the fields does take a little getting used to; evidently it's necessary since IE doesn't implement property setters and getters.  Other than the new syntax, our class is pretty basic.  We have a started field and a totalSeconds field that drive the rest of our fields by way of the dependentObservable() functionality.  The dependentObservable() is a nifty way of declaring fields that are computed based on other observable() fields.  For us, we do some quick math to determine our seconds, minutes and hours based on the totalSeconds that have passed while running the timer.  We also create some fields for the angle of our timer hands based on the computed underlying second/minute/hour values.


The ViewModel and View DataBinding

@{
 Page.Title = "Home Page";
}

@section ScriptSection {

    <script id="faceTemplate" type="text/x-jquery-tmpl">
        @* Our SVG Code in a partial view *@
        @Html.Partial("_WatchFace")
        <div id="timerInfo" style="font-weight: bold; font-size: 24px; float: left;">
            <span class="minutes" data-bind="text: minutes()"></span>
            <span>:</span>
            <span class="seconds" data-bind="text: secondsDisplay()"></span>
        </div>
    </script>

    <script type="text/javascript">

        // Our custom svg Rotate transform binding...
        ko.bindingHandlers.svgRotate = {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
                // This will be called when the binding is first applied to an element
                // Set up any initial state, event handlers, etc. here
            },
            update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
                // This will be called once when the binding is first applied to an element,
                // and again whenever the associated observable changes value.
                // Update the DOM element based on the supplied values here.

                var value = valueAccessor(), allBindings = allBindingsAccessor();

                var rotation = ko.utils.unwrapObservable(value);
                var originX = allBindings.originX || 0;
                var originY = allBindings.originY || 0;

                var rotateText = 'rotate(' + rotation + ', ' + originX + ', ' + originY + ')';
                var id = $(element).attr('id');

                // Using the old school doc getElement because jquery's attr() is not setting the value correctly
                var elem = document.getElementById(id);
                if (!elem) { log('rotate binding element not found'); return; }

                elem.setAttribute('transform', rotateText);
            }
        };

        // Our page ViewModel
        var viewModel = {
            watch: new ko.observable(new timer()),
            start: function () {
                this.watch().start();
            },
            stop: function () {
                this.watch().stop();
            },
            toggleTimer: function () {
                this.watch().started() ? this.stop() : this.start();
            }
        };

        ko.applyBindings(viewModel);
    </script>
}

<p>
    This is an example project using <a href="http://html5boilerplate.com">HTML5 Boilerplate</a> patterns, <a href="http://knockoutjs.com">Knockout.js</a>  MVVM Binding and Templating, <a href="http://docs.jquery.com/Qunit">QUnit</a> unit tests and SVG Graphics.
</p><br />

<div id="watchContainer" style="cursor: pointer;" data-bind='template: { name: "faceTemplate", data: watch()}, click: toggleTimer'>

</div>

The main parts of this code are the ViewModel which we bind our HTML Elements to, and the jQuery templates that define our stopwatch and second/minute hands.  At the top, I declare my SVG Stopwatch with a custom knockout binding (you can see the code for the special binding at lines 10-20) I created to update the transform of the path based on the angle in the timer.

Next, the script section for our page creates a special binding that updates the transform attribute of the Path SVG Element to rotate the hands of the clock.  Also in the script section, we declare our ViewModel for the page.  The ViewModel creates a timer and some utility functions for toggling the timer between start and stop.  After declaring our ViewModel, we use Knockout's ko.applyBindings() function to setup the page's templates and apply our data bindings.


The MotherEffin Clock Demo Project

I've put together a Demo Project for download using my MotherEffin HTML5 Boilerplate project template for MVC3.  I highly recommend visiting the KnockoutJS documentation for more information about templatescustom bindings and observables.

Next time, we'll go through Unit Testing and Object Oriented Javascript with QUnit.


Now Playing - LL Cool J - Momma Said Knock You Out

Saturday, February 12, 2011

Silverlight Fade Control Behavior - FadeyBehavior

In my last post about Silverlight Property Triggers and Storyboard Actions I showed a way for us to fade in an element when a property on our view model was a certain value.  We used this to fade in a list of items after we loaded them, and ultimately we could fade them out while we were re-loading more items.  The code looked kind of like this;

<ItemsControl
    Margin="0 130 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    Opacity="0.0"
    ItemsSource="{Binding Items}">
            <i:Interaction.Triggers>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="True">
                    <local:StoryboardAction>
                        <Storyboard>
                            <DoubleAnimation
                                To="1.0"
                                Duration="00:00:0.7"
                                Storyboard.TargetProperty="Opacity" />
                        </Storyboard>
                    </local:StoryboardAction>
                </local:BooleanPropertyTrigger>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="False">
                    <local:StoryboardAction>
                        <Storyboard>
                            <DoubleAnimation
                                To="0.0"
                                Duration="00:00:0.4"
                                Storyboard.TargetProperty="Opacity" />
                        </Storyboard>
                    </local:StoryboardAction>
                </local:BooleanPropertyTrigger>
            </i:Interaction.Triggers>
                <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border
                Height="30"
                Width="200"
                Margin="0 2"
                BorderBrush="Plum"
                BorderThickness="3"
                CornerRadius="5">
                <TextBlock
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Margin="5 0 0 0"
                    Text="{Binding Name}" />
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Now, the more I looked at all that XAML the more I thought we could make this a lot simpler.  By the end of this post we will hopefully end up having something much simpler to use; like this;

<ItemsControl
    Margin="0 130 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    Opacity="0.0"
    ItemsSource="{Binding Items}">
    <i:Interaction.Behaviors>
        <local:FadeyBehavior
            Binding="{Binding FinishedLoading}" />
    </i:Interaction.Behaviors>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border
                Height="30"
                Width="200"
                Margin="0 2"
                BorderBrush="Plum"
                BorderThickness="3"
                CornerRadius="5">
                <TextBlock
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Margin="5 0 0 0"
                    Text="{Binding Name}" />
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

So, what we've done is encompassed our two triggers and their storyboards into a new Behavior.  Our new FadeyBehavior creates and attaches our to FadeIn and FadeOut triggers along with the associated FadeInAction and FadeOutAction.  The secret sauce is in cascading the binding down to the triggers in the code behind.

using System.Windows;
using System.Windows.Data;
using System.Windows.Interactivity;

/// <summary>
/// A behavior for fading an element based on whether a bound property is true or false.
/// </summary>
public class FadeyBehavior : Behavior<FrameworkElement>
{
    /// <summary>
    /// The <see cref="Binding" /> dependency property's name.
    /// </summary>
    public const string BindingPropertyName = "Binding";

    /// <summary>
    /// Gets or sets the value of the <see cref="Binding" />
    /// property. This is a dependency property.
    /// </summary>
    public object Binding
    {
        get
        {
            return (object)GetValue(BindingProperty);
        }
        set
        {
            SetValue(BindingProperty, value);
        }
    }

    /// <summary>
    /// Identifies the <see cref="Binding" /> dependency property.
    /// </summary>
    public static readonly DependencyProperty BindingProperty = DependencyProperty.Register(
        BindingPropertyName,
        typeof(object),
        typeof(FadeyBehavior),
        new PropertyMetadata(null));


    protected override void OnAttached()
    {
        if (AssociatedObject.Opacity != 0.0)
            AssociatedObject.Opacity = 0.0;

        // Create our fade in/out triggers
        var triggerIn = new FadeInTrigger();
        var triggerOut = new FadeOutTrigger();

        // Bind the Binding property in this behavior to the underlying triggers.
        var b = new Binding("Binding") { Source = this };
        BindingOperations.SetBinding(triggerIn, BooleanPropertyTrigger.BindingProperty, b);
        BindingOperations.SetBinding(triggerOut, BooleanPropertyTrigger.BindingProperty, b);

        // Add our triggers to the associated object.
        var currTriggers = Interaction.GetTriggers(AssociatedObject);
        currTriggers.Add(triggerIn);
        currTriggers.Add(triggerOut);

        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
    }
}

For the time being, I've only had to use this for a boolean property trigger, but it could be expanded into a base class for other trigger types (String, DateTime range, WidgetA, etc.).

You can download the code to mess around with it yourself.

Now Playing - La Roux - In For The Kill

Friday, February 11, 2011

Silverlight Storyboard and FadeIn FadeOut Actions

In my last post I talked about Silverlight Property Triggers and how we could use them to trigger functionality when a property on our ViewModel was a certain value.  The keen observer will have noted that I glossed over a bit of magic that was happening in the StoryboardAction.  Today's post is going to cover how to create your very own StoryboardAction to love and cherish for all time.  Here is a quick re-cap of what our XAML looked like in the last post;

<ItemsControl
    Margin="0 130 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    Opacity="0.0"
    ItemsSource="{Binding Items}">
            <i:Interaction.Triggers>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="True">
                    <local:StoryboardAction>
                        <Storyboard>
                            <DoubleAnimation
                                To="1.0"
                                Duration="00:00:0.7"
                                Storyboard.TargetProperty="Opacity" />
                        </Storyboard>
                    </local:StoryboardAction>
                </local:BooleanPropertyTrigger>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="False">
                    <local:StoryboardAction>
                        <Storyboard>
                            <DoubleAnimation
                                To="0.0"
                                Duration="00:00:0.4"
                                Storyboard.TargetProperty="Opacity" />
                        </Storyboard>
                    </local:StoryboardAction>
                </local:BooleanPropertyTrigger>
            </i:Interaction.Triggers>
                <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border
                Height="30"
                Width="200"
                Margin="0 2"
                BorderBrush="Plum"
                BorderThickness="3"
                CornerRadius="5">
                <TextBlock
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Margin="5 0 0 0"
                    Text="{Binding Name}" />
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Today, we added another trigger to fade the items out when we re-load our items.  Then, we are going to encapsulate the fade animations in a new action that will reduce our XAML a little bit.

First, lets take a quick look at the StoryboardAction Source.

using System;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Markup;
using System.Windows.Media.Animation;

[ContentProperty("Story")]
public class StoryboardAction : TriggerAction<FrameworkElement>
{
    /// <summary>
    /// Holder for a target setting state.
    /// </summary>
    private bool hasTargetSet = false;

    /// <summary>
    /// Gets or sets the storyboard for this action.
    /// </summary>
    /// <value>The storyboard to run when invoked.</value>
    public Storyboard Story { get; set; }

    /// <summary>
    /// The <see cref="Target" /> dependency property's name.
    /// </summary>
    public const string TargetPropertyName = "Target";

    /// <summary>
    /// Gets or sets the value of the <see cref="Target" />
    /// property. This is a dependency property. 
    /// You can leave this un-set if you want to use the Trigger's 
    /// AssociatedObject as the Target.
    /// </summary>
    public DependencyObject Target
    {
        get
        {
            return (DependencyObject)GetValue(TargetProperty);
        }
        set
        {
            SetValue(TargetProperty, value);
        }
    }

    /// <summary>
    /// Identifies the <see cref="Target" /> dependency property.
    /// </summary>
    public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
        TargetPropertyName,
        typeof(DependencyObject),
        typeof(StoryboardAction),
        new PropertyMetadata(null));
        

    /// <summary>
    /// Invokes the action.
    /// </summary>
    /// <param name="parameter">The parameter to the action. If the action does not require a parameter, the parameter may be set to a null reference.</param>
    protected override void Invoke(object parameter)
    {
        if (this.Story == null)
            return;

        if(!hasTargetSet)
        {
            // Fall back to the associated object if no target defined.
            var target = this.Target ?? AssociatedObject;
            if (target != null)
            {
                Storyboard.SetTarget(this.Story, target);
                hasTargetSet = true;
            }
        }

        // Stop any previously ran storyboards.
        if (this.Story.GetCurrentState() != ClockState.Stopped)
            this.Story.Stop();
            
        this.Story.Begin();            
    }
}

The important parts of this class are the Invoke logic.  We are doing some basic null checking, then setting the target for our animation to either the Target or the AssociatedObject if no Target was found.  Finally, we make sure the animation is not currently running, then start up our animation.

Extra Snazzy Bonus Implementation Codez


I feel like we are really connecting here, so I'm gonna throw in some extra snazzy bonus material just for you.  Here are some short hand versions of our Opacity Fade Animations that should tame the XAML Monster.



using System;
using System.Windows;
using System.Windows.Media.Animation;

public class FadeOutAction : FadeAction
{
    public FadeOutAction()
        : base(.2, 0.0)
    { }
}

public class FadeInAction : FadeAction
{
    public FadeInAction()
        : base(.6, 1.0)
    { }
}

public class FadeAction : StoryboardAction
{

    public FadeAction(double durationSeconds = .6, double fadeTo = 0.0)
    {
        var anim = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(durationSeconds)), To = fadeTo };
        Storyboard.SetTargetProperty(anim, new PropertyPath("Opacity"));

        this.Story = new Storyboard();
        this.Story.Children.Add(anim);
    }
}

Now our XAML is looking slightly better.

<ItemsControl
    Margin="0 130 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    Opacity="0.0"
    ItemsSource="{Binding Items}">
            <i:Interaction.Triggers>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="True">
                    <local:FadeInAction />
                </local:BooleanPropertyTrigger>
                <local:BooleanPropertyTrigger
                    Binding="{Binding FinishedLoading}"
                    TriggerValue="False">
                    <local:FadeOutAction />
                </local:BooleanPropertyTrigger>
            </i:Interaction.Triggers>
                <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border
                Height="30"
                Width="200"
                Margin="0 2"
                BorderBrush="Plum"
                BorderThickness="3"
                CornerRadius="5">
                <TextBlock
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left"
                    Margin="5 0 0 0"
                    Text="{Binding Name}" />
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Next time, we will create a new FadeyBehavior to encapsulate our fading triggers.

Now Playing - Jay Z, Rick Ross - Hustlin Remix


Wednesday, February 9, 2011

Silverlight 4 Property Triggers

I spent a little time this week messing around with the newly added Triggers and TriggerActions available through the new Expression Blend 4 SDK.

Triggers and Behaviors are really just ways to attach functionality to an existing element, and the base classes that are included in the newer version of Silverlight 4 really make the job easier.  I'm going to walk through adding a trigger that fires when one of the properties on my ViewModel changes to true.  Now allegedly there is an existing trigger (DataStoreChangedTrigger) that will fire actions based on when a bound property changes, but I want to only fire my actions when my bound property becomes a specific value.

Our Goal

<ItemsControl
    Margin="0 130 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Top"
    Opacity="0.0"
    ItemsSource="{Binding Items}">
    <i:Interaction.Triggers>
        <local:BooleanPropertyTrigger
            Binding="{Binding FinishedLoading}"
            TriggerValue="True">
            <local:StoryboardAction>
                <Storyboard>
                    <DoubleAnimation
                        To="1.0"
                        Duration="00:00:0.7"
                        Storyboard.TargetProperty="Opacity" />
                </Storyboard>
            </local:StoryboardAction>
        </local:BooleanPropertyTrigger>
    </i:Interaction.Triggers>
</ItemsControl>

The Codez

[Download the PropertyTrigger Example Source Project and play along at home]

To start out with, I create a base PropertyChangedTrigger class that will do most of the heavy lifting for us.  Essentially, we want to inherit from the TriggerBase<...> generic base class and specify that we want our Trigger to attach to a FrameworkElement (I suppose you could use another type of control class, but FrameworkElement will encompass just about any element with a DataContext, which I find useful).  Our PropertyChangedTrigger will expose a Binding property that will allow us to attach an event handler when our bound property changes so we can invoke our TriggerActions.

/// <summary>
/// A base property changed trigger that
/// fires whenever the bound property changes.
/// </summary>
public class PropertyChangedTrigger : TriggerBase<FrameworkElement>
{
    /// <summary>
    /// The <see cref="Binding" /> dependency property's name.
    /// </summary>
    public const string BindingPropertyName = "Binding";

    /// <summary>
    /// Gets or sets the value of the <see cref="Binding" />
    /// property. This is a dependency property.
    /// </summary>
    public object Binding
    {
        get
        {
            return (object)GetValue(BindingProperty);
        }
        set
        {
            SetValue(BindingProperty, value);
        }
    }

    /// <summary>
    /// Identifies the <see cref="Binding" /> dependency property.
    /// </summary>
    public static readonly DependencyProperty BindingProperty =
        DependencyProperty.Register(
        BindingPropertyName,
        typeof(object),
        typeof(PropertyChangedTrigger),
        new PropertyMetadata(null,
            new PropertyChangedCallback(Binding_ValueChanged)));

    /// <summary>
    /// Called after the trigger is attached to an AssociatedObject.
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();
    }

    /// <summary>
    /// Called when the trigger is being detached
    /// from its AssociatedObject,
    /// but before it has actually occurred.
    /// </summary>
    protected override void OnDetaching()
    {
        base.OnDetaching();
    }

    /// <summary>
    /// Occurs when Binding's value changes.
    /// </summary>
    /// <param name="obj">The obj on which the binding changed.</param>
    /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
    private static void Binding_ValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var trig = obj as PropertyChangedTrigger;
        if (trig != null && trig.ShouldTriggerFire(args.NewValue))
        {
            trig.OnPropertyTrigger(args.NewValue);
        }
    }

    /// <summary>
    /// Does the change logic test. By default, it will always fire on value change.
    /// </summary>
    /// <param name="newValue">The new value.</param>
    /// <returns>True if the trigger should fire, otherwise false.</returns>
    protected virtual bool ShouldTriggerFire(object newValue)
    {
        return true;
    }

    /// <summary>
    /// Called when [property trigger].
    /// </summary>
    /// <param name="value">The value of the property.</param>
    protected virtual void OnPropertyTrigger(object value)
    {
        base.InvokeActions(value);
    }
}

As you can tell, our PropertyChangedTrigger makes use of a virtual method ShouldTriggerFire(…) that will default to just fire everytime a property changes value. Next, we will override our base class to create an EqualsPropertyTrigger that only fires when the value changes to a specific one that we want.

/// <summary>
/// A base class for property triggers that must be equal to fire.
/// </summary>
/// <typeparam name="TValue">The type of the trigger value.</typeparam>
/// <summary>
/// A base class for property triggers that must be equal to fire.
/// </summary>
/// <typeparam name="TValue">The type of the trigger value.</typeparam>
public class EqualsPropertyTrigger<TValue> : PropertyChangedTrigger
{
    /// <summary>
    /// Gets or sets the trigger value to match the property value for.
    /// </summary>
    /// <value>The trigger value.</value>
    public TValue TriggerValue { get; set; }

    /// <summary>
    /// Logic to check whether the trigger should fire.
    /// </summary>
    /// <param name="newValue">The new value.</param>
    /// <returns>True if the trigger should fire, otherwise false.</returns>
    protected override bool ShouldTriggerFire(object newValue)
    {
        if (newValue == null)
            return this.TriggerValue == null;

        return newValue.Equals(this.TriggerValue);
    }
}

So now we have a nice base class for our BooleanPropertyTrigger that makes it's implementation really nice and clean.

/// <summary>
/// A generic object property trigger
/// </summary>
public class PropertyTrigger : EqualsPropertyTrigger<object>
{ }

/// <summary>
/// A Boolean value property trigger
/// </summary>
public class BooleanPropertyTrigger : EqualsPropertyTrigger<bool>
{ }

/// <summary>
/// A string property value trigger
/// </summary>
public class StringPropertyTrigger : EqualsPropertyTrigger<string>
{ }

Now, all that's left to do is hook it up in our XAML by adding the namespace to our trigger and making sure we have a reference to System.Windows.Interactivity (version 4.0.5.0).

Next Steps

Next, we could make a NotEqualsPropertyTrigger that fires when a value is not a certain value. It's implementation would be as easy as inheriting from the EqualsPropertyTrigger and negating the base ShouldFireTrigger(...) method.

Hopefully, like me, you've learned a little about triggers and how they can be useful. For my next blog post I'm going to incorporate the visual state manager and make a GoToStateAction along with talking a little bit about creating the StoryBoardAction you see in the example.

Now Playing - Pretty Lights - Hot Like Sauce