Rediscovering the Obvious

…stumbling in the footsteps of greatness

EntityCommandShim<T> – Silverlight MVVM helper

without comments

I received a question yesterday around the shim pattern that I used, and I thought I’d clean it up a bit and share it. Again, if you’re used to reading about agile and lean here, this isn’t one of those posts (in case the title didn’t make that clear).

Refresher

The situation I’m addressing is that I have an element in my XAML that uses an ItemsSource (e.g. ItemsControl, ListBox) and want to receive an event specific to one item. This post will assume that I’m working with an items template like this, which will be bound against a DTO or Entity that has a property descriptively named “Value” containing the content to display.

<DataTemplate x:Key="TemplateSelectionItem">
   <HyperlinkButton Content="{Binding Value}" 
   />
</DataTemplate>

Now, whenever the user activates the hyperlink, the “Click” event will be raised. Without MVVM, I’d just add a handler in my code-behind that attaches to the click event and performs the logic. The testability and separation of concerns aspects of MVVM suggest this is a very bad idea. Instead, we should set up something that is called in response to the click. This leads to two options: put handling code on the bound entity, or put handling code in the ViewModel containing the ItemsControl. Since polluting the entity seems like a bad idea for a behavior local to a single view, let’s plan on putting it in in the ViewModel. And, since I like command objects for the flexibility they offer for a wide variety of scenarios, let’s target calling a Command instead of a method. Avoiding Prism-specific code, I’d write something like this:

        <DataTemplate x:Key="TemplateSelectionItem">
            <HyperlinkButton Content="{Binding Value}" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <i:InvokeCommandAction Command="{Binding Command}" CommandParameter="{Binding }" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </HyperlinkButton>
        </DataTemplate>

Unfortunately, the “Command” binding will always fail, because the DataContext is currently set to my entity. Well, that’s fine, let’s just use a named source like this:

        <DataTemplate x:Key="TemplateSelectionItem">
            <HyperlinkButton Content="{Binding Value}" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <i:InvokeCommandAction Command="{Binding DataContext.Command, ElementName=myListBox}" CommandParameter="{Binding }" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </HyperlinkButton>
        </DataTemplate>

Unfortunately, this is entirely too brilliant to keep working. It’ll work great as long as you don’t modify the ItemsPanel of your ListBox [1]. As soon as you do that, the binding will fail, and it took me far too long to figure out why the first time. Remembering back to my early programming education when I was taught things like “there’s no problem that can’t be solved with another level of indirection”, I decided to just put something in the middle that puts the Command where I need it. Thus the introduction of the shim, or as it’s now called, the EntityCommandShim. The comment prompted me to do some aggressive refactoring of what I was using, so thank you… here’s what it looks like now:

    public class EntityCommandShim<TEntity>
    {
        public EntityCommandShim(TEntity sourceObject, DelegateCommand<object> command)
        {
            Debug.Assert(command != null && sourceObject != null);

            this.Command = command;
            this.Entity = sourceObject;
        }
        public DelegateCommand<object> Command { get; private set; }
        public TEntity Entity { get; private set; }
    }

There’s not much to it. The Command and Entity references aren’t meant to change for the lifetime of the object, so no INotifyPropertyChanged implementation. The passed in Command is just a reference to one declared on the ViewModel, and doing the wrapping is quite simple with a line like this:

            this.AvailableTemplates = service.AvailableTemplates.Select(item =>
                new EntityCommandShim<FilterData>(item, TemplateItemSelectedCommand)).ToObservableCollection();

Once this is done, I’m all set. The ItemsTemplate becomes: [2]

        <DataTemplate x:Key="TemplateSelectionItem">
            <HyperlinkButton Content="{Binding Entity.Value}" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <i:InvokeCommandAction Command="{Binding Command}" CommandParameter="{Binding Entity }" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </HyperlinkButton>
        </DataTemplate>

and the repeater can be as simple as this:

            <ItemsControl
                ItemTemplate="{StaticResource TemplateSelectionItem}"
                ItemsSource="{Binding AvailableTemplates}"
                >
            </ItemsControl>

Or as complicated as this: [3]

<input:AutoCompleteBox 
    FilterMode="Contains" 
    MinimumPopulateDelay="250"
    ItemsSource="{Binding Clients}"
    SelectedItem="{Binding SelectedClient, Mode=TwoWay}"
    ItemTemplate="{StaticResource ProviderSelectionItem}"
    IsDropDownOpen="{Binding IsSelectionActive, Mode=TwoWay}"
    ValueMemberPath="Entity.Value"
>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged" SourceName="autoCompleteBox" >
            <ic2:InvokeCommandWithArgsAction 
                Command="{Binding SelectionChangedCommand}" 
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}"
                />
        </i:EventTrigger>
        <i:EventTrigger EventName="DropDownClosed" SourceName="autoCompleteBox" >
            <ic2:InvokeCommandWithArgsAction 
                Command="{Binding SelectionConfirmedCommand}" 
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}"
                />
        </i:EventTrigger>
        <i:EventTrigger EventName="DropDownOpened" SourceName="autoCompleteBox" >
            <ic2:InvokeCommandWithArgsAction 
                Command="{Binding BeginSelectionCommand}" 
                CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}"
                />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</input:AutoCompleteBox>

[1] I haven’t tried this with ItemsControl, and I don’t know where this issue comes from… any thoughts? I also haven’t tried this in SL4, maybe it’s fixed? Will check that out soon, and if it works, all of this can be removed, making it a true “shim” in the sense that it goes away when the floor’s fixed.

[2] When I need to synchronize against SelectedItem in a list box, I often use CommandParameter={Binding} so I get the instance of the shim to reassign back to a property that’s Mode=TwoWay bound against SelectedItem.

[3] InvokeCommandWithArgsAction is adapted from http://weblogs.asp.net/alexeyzakharov/archive/2010/03/24/silverlight-commands-hacks-passing-eventargs-as-commandparameter-to-delegatecommand-triggered-by-eventtrigger.aspx and is a way of passing the arguments of an event as the CommandParameter. Check it out!

Written by erwilleke

October 25th, 2010 at 3:15 pm

Posted in Uncategorized

Leave a Reply