Silverlight 3 Behaviors – Double click selection on a DataGrid

by Tom 15. July 2009 17:45

If you hadn’t noticed, behaviors created in Silverlight 3 beta will be broken in Silverlight 3 release. The reason for this is that the assembly Microsoft.Expression.Interactivty.dll (that used to be found in C:\Program Files\Microsoft Expression\Blend 3\Libraries\Silverlight\v3.0) no longer exists. Instead, it’s replaced by System.Windows.Interactivity.dll (which is found in C:\Program Files\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight). Seems simple enough, sure, but it took a good hour (with fists clenched) for me to work this out today since I couldn’t find any docs on it at all.

So, to turn this rant into something useful, I’ll talk about a useful trigger for grabbing a double click on UIElements (such as DataGrid).

The scenario is you have a list of items displayed in a DataGrid and want to select one by double clicking it. We’ll need to code two classes: 1. a trigger that fires on double click; 2. an action that can be fired in response to the trigger (e.g. an action that executes an ICommand).

The first step, is to create the trigger .cs file. Visual Studio 2008 and Blend 3 now provide templates for Triggers, Actions & Behaviors to get you started. Here’s what the template looks like in Visual Studio:

15-07-2009 10-11-13 PM

This creates the class, adds the necessary overrides and adds a reference to System.Windows.Interactivity.dll.

Now to implement the double click trigger. There’s not a lot of mystery about how to do this, and I borrowed the logic from this useful blog post: Silverlight 3 Behaviors : Double Click Trigger – but extended it in several key areas.

Firstly, I used UIElement.Addhandler() to attach the mouse events (MouseLeftButtonDown, MouseLeftButtonUp) in order to ensure the trigger works on UIElements that mark these events handled (like Button for instance).

Secondly, I wanted to be selective and intelligent about the parameter passed in to TriggerBase.InvokeActions(). The important thing here is that, in different circumstances, I’ll want to pass in different parameters. For instance, if the control that fired the trigger is a DataGrid, I’ll want to pass in the SelectedItem; if the control is a Selector, I’ll want to pass in the SelectedItem; or if the control is something else, I may want to pass in the DataContext.

In order to be as generic and extensible as possible, I decided to build a base class that I could inherit other classes from. Here’s my implementation:

using System;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace GridClicker.Behaviors
{
    public class DoubleClickTriggerBase : TriggerBase<UIElement>
    {
        private readonly DispatcherTimer _timer;

        public DoubleClickTriggerBase()
        {
            _timer = new DispatcherTimer
            {
                Interval = new TimeSpan(0, 0, 0, 0, 200)
            };
            _timer.Tick += OnTimerTick;
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent,
                                        new MouseButtonEventHandler(OnMouseAction), true);
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent,
                                           new MouseButtonEventHandler(OnMouseAction));
            if (_timer.IsEnabled)
            {
                _timer.Stop();
            }
        }

        private void OnMouseAction(object sender, MouseButtonEventArgs e)
        {
            if (!_timer.IsEnabled)
            {
                _timer.Start();
                return;
            }
            _timer.Stop();
            DoInvoke();
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            _timer.Stop();
        }

        protected virtual void DoInvoke()
        {
            InvokeActions(null);
        }
    }
}

The key thing here is the virtual DoInvoke() method that can be overridden in descendent classes if a special value need to be passed as parameter to InvokeActions().

Here’s an inherited class, DataGridDoubleClickTrigger.cs:

using System.Windows.Controls;

namespace GridClicker.Behaviors
{
    public class DataGridDoubleClickTrigger : DoubleClickTriggerBase
    {
        protected override void DoInvoke()
        {
            var dg = AssociatedObject as DataGrid;
            var o = dg == null ? null : dg.SelectedItem;
            if (o != null)
            {
                InvokeActions(o);
            }
        }
    }
}

This simply passes in the SelectedItem of the DataGrid as the InvokeAction() parameter. Of course you could make any number of classes that inherit from DoubleClickTriggerBase. I should point out that there is an assumption here that the user will double click on a row of the DataGrid and not the header, though it will not break in any case. If a user does double click on the header of the DataGrid, if the SelectedItem is not null it will be returned, otherwise nothing will happen.

Now a trigger is no good without an action to fire, so here is a very quick and simplistic action that executes an ICommand. Firstly, create the action in Visual Studio:

15-07-2009 11-27-48 PM

And here’s the implementation:

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace GridClicker.Behaviors
{
    public class ExecuteCommandAction : TriggerAction<FrameworkElement>
    {
        public string CommandName { get; set; }

        protected override void Invoke(object o)
        {
            if (CommandName == null) return;
            var dataContext = AssociatedObject.DataContext;
            if (dataContext == null) return;
            var command = dataContext.GetType().GetProperty(CommandName).GetValue(dataContext, null) as ICommand;
            if (command != null && command.CanExecute(o))
            {
                command.Execute(o);
            }
        }
    }
}

Even though TriggerAction is a DependencyObject, it doesn’t seem to want to let you make CommandName a DependencyProperty and bind it’s value. No idea why this is, hence the roundabout route to grab the command.

To put it altogether, here is the XAML:

<StackPanel x:Name="ContentStackPanel">

    <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" 
                       Text="Home"/>

    <data:DataGrid ItemsSource="{Binding Categories}" IsReadOnly="True">
        <i:Interaction.Triggers>
            <b:DataGridDoubleClickTrigger>
                <b:ExecuteCommandAction CommandName="SelectCommand" />
            </b:DataGridDoubleClickTrigger>
        </i:Interaction.Triggers>
    </data:DataGrid>
    
    <Button Content="Double Click Button" Margin="10" VerticalAlignment="Center" HorizontalAlignment="Center">
        <i:Interaction.Triggers>
            <b:DoubleClickTriggerBase>
                <b:ExecuteCommandAction CommandName="ButtonCommand"/>
            </b:DoubleClickTriggerBase>
        </i:Interaction.Triggers>
    </Button>

</StackPanel>

Note that I also put in a Button that demonstrates that the base class – DoubleClickTriggerBase - is useful in instances where you have no need to pass a parameter to the InvokeActions().

Finally, to round things off, here is the view’s presenter:

public class HomePresenter : INotifyPropertyChanged
{
    public ICommand SelectCommand { get; private set; }
    public ICommand ButtonCommand { get; private set; }

    public HomePresenter()
    {
        SelectCommand = new DelegateCommand<object>(o =>
        {
            var cat = o as Category;
            var dw = new DetailsWindow {Width = 300.0, Height = 300.0};
            var g = dw.FindName("LayoutRoot") as Grid;
            g.Children.Add(new TextBlock() 
            { 
                Text = "Category Name: " + cat.CategoryName,
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center
            });
            dw.Show();
        });

        ButtonCommand = new DelegateCommand<object>(o => HtmlPage.Window.Alert("Button was double clicked!"));
    }

    public ObservableCollection<Category> Categories
    {
        get { return CategoryHelper.GetCategories(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

 

In the example I am using Prism’s DelegateCommand implementation of ICommand.

There you have it!

Source Code

Categories: Work

Getting confirmation on an ICommand

by Tom 9. July 2009 18:10

A question I asked myself the other day was 'what is the most elegant way to show a confirm dialogue to a user?' Now there's nothing hard about
doing this and we all do it from time to time, but nevertheless there are many different ways one might go about doing it. My particular concern
was firstly to invoke ICommands from the user action and secondly avoid triggering UI events from my presenter (i.e. opening dialogues).

The first and most obvious technique is to simply show a confirm in the event handler of the button in code behind.

Take the following XAML:

<data:DataGrid ItemsSource="{Binding Categories}" AutoGenerateColumns="False">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="ID" Binding="{Binding CategoryID}" />
        <data:DataGridTextColumn Header="Name" Binding="{Binding CategoryName}" />
        <data:DataGridTemplateColumn Header="">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Delete" Click="Button_Click" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

and the click handler in the code-behind:

private void Button_Click(object sender, RoutedEventArgs e)
{
	Category cat = (sender as Button).DataContext as Category;    
	if (HtmlPage.Window.Confirm("Are you sure you want to delete that?"))    
	{        
		HtmlPage.Window.Alert("Deleted");    
	}
}

This works perfectly well, and looks something like this:

9-07-2009 9-31-01 PM

Not a good solution in my case, however, as I wanted to use commanding to link my Delete button to an ICommand in the view’s presenter. (Prism gives a great implementation of ICommandDelegateCommand<T>).

When you use commanding, the obvious place to put the confirm dialogue is in the presenter:

<data:DataGrid ItemsSource="{Binding Categories}" AutoGenerateColumns="False">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="ID" Binding="{Binding CategoryID}" />
        <data:DataGridTextColumn Header="Name" Binding="{Binding CategoryName}" />
        <data:DataGridTemplateColumn Header="">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Delete" cal:Click.Command="{Binding Path=Value, Source={StaticResource DeleteCommand}}" 
                            cal:Click.CommandParameter="{Binding}"/>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

and the DelegateCommand in the presenter:

DeleteCommand = new DelegateCommand<Category>((cat) =>
    {
        if (HtmlPage.Window.Confirm("Are you sure you want to delete that?"))
        {
            HtmlPage.Window.Alert("Deleted");
        }
    });

NOTE: Of course there’s an obvious problem with binding to an ICommand inside a DataTemplate – the DataContext is not the view (e.g. the view model or presenter) but the particular data item that the DataTemplate is displaying. You can get around this using RelativeSource binding in WPF, but not in Silverlight. The solution is to make your ICommand a StaticResource using a implementation of ObservableCommand, as described here: http://msdn.microsoft.com/en-us/library/dd458928.aspx

Now in the example above, the confirm dialogue certainly displays as I’d like it to, but I have philosophical objections to taking UI components (i.e. the dialogue) out of the view and sticking it in the presenter. Doing this also makes unit testing in the presenter a lot more difficult.

The first alternative that came to mind was to use the code-behind again, but this time invoke the ICommand from there, like this:

private void Click1(object sender, RoutedEventArgs e)
{
    ((HomePagePresenter)DataContext).DeleteCommand.Execute((sender as Button).DataContext as Category);
}

This is actually a fairly nice solution- the separation between the presenter and the view is maintained, the inconvenience of declaring the ICommand as a StaticResource is avoided, and it’s quite intuitive. It still seems a little inelegant, however – since all XAML purists will tell you that binding is better in every case.

The next solution I implemented is really nice. I created a new attached behaviour that extends Prism’s CommandBehaviorBase<T> called ConfirmClick. This lets me use the following mark-up:

<data:DataGrid ItemsSource="{Binding Categories}" AutoGenerateColumns="False" Grid.Row="1">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="ID" Binding="{Binding CategoryID}" />
        <data:DataGridTextColumn Header="Name" Binding="{Binding CategoryName}" />
        <data:DataGridTemplateColumn Header="">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Delete" cal2:ConfirmClick.Command="{Binding Path=Value, Source={StaticResource DeleteCommand}}"
                            cal2:ConfirmClick.CommandParameter="{Binding}"/>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

I won’t include the two files ButtonBaseConfirmClickCommandBehavior.cs & ConfirmClick.cs here as they are quite lengthy, but download the source code at the end of this post if you want them.

This approach totally abstracts the out the creation of the confirm dialogue so I don’t have to think about it at all. It’s neat, reusable and fits the pattern perfectly.

I could have stopped here, but there was one other avenue I wanted to check: the new Behaviours that come as part of Silverlight 3. Behaviours are divided into three broad categories: Triggers, Actions and Behaviors. In my case, I can use the inbuilt EventTrigger trigger and attach it to the Click event of my Button.

Don’t forget that if you want to use Behaviors, you need to reference Microsoft.Expression.Interactivity.dll from you project and import the namespace into the XAML.

Now that I can trap the Interestingly, the Microsoft.Expression.Interactivity implements an InvokeCommandAction which I couldn’t for the life of me get to work. It doesn’t help that it is not mentioned anywhere in the Silverlight 3 documentation. Not to worry, I ended up implementing my own Action which looks for an ICommand in the FrameworkElement.Tag property. Here’s the XAML:

<data:DataGrid ItemsSource="{Binding Categories}" AutoGenerateColumns="False" Grid.Row="2">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="ID" Binding="{Binding CategoryID}" />
        <data:DataGridTextColumn Header="Name" Binding="{Binding CategoryName}" />
        <data:DataGridTemplateColumn Header="">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Delete" Tag="{Binding Path=Value, Source={StaticResource DeleteCommand}}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Click">
                                <loc:ConfirmCommandAction />
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </Button>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

And the Action looks like this:

public class ConfirmCommandAction : TriggerAction<FrameworkElement>
{
    private string message;
    public string Message
    {
        get { return message ?? "Are you sure you want to do that?"; }
        set { message = value; }
    }
    protected override void Invoke(object parameter)
    {
        if (AssociatedObject.Tag is ICommand)
        {
            var cmnd = AssociatedObject.Tag as ICommand;
            if (cmnd != null && cmnd.CanExecute(AssociatedObject.DataContext))
            {
                if (HtmlPage.Window.Confirm(Message))
                {
                    cmnd.Execute(AssociatedObject.DataContext);
                }
            }
        }
    }
}

I should point out that my implementation of ConfirmCommandAction was pretty rapid  and has some holes in it. For instance, I’m not sure I like assuming that the DataContext of the AssociatedObject should be passed as the parameter of the ICommand. I’m also a little unsure whether the Tag property was really supposed to be used like that. Nevertheless it all works quite nicely.

So, there’s several techniques for implementing a confirm dialogue on an ICommand. I think each is valid in it’s own way, but my money is on the ConfirmClick attached behaviour as the most elegant solution – providing you’re willing to include some of the Prism libraries in your project.

Hope you find it useful!

Get the source

Categories: Work

UserState & knowing when you’re done in .NET RIA Services

by Tom 7. July 2009 17:28

UPDATE: I bumped into Nikhil Kothari on Twitter and he mentioned that the eventing model for DomainContext is changing in the July CTP (going to become a lot more intuitive when dealing with multiple concurrent calls)…

The more time I spend working with .NET RIA services, the more I like it. That said, it’s not without it’s frustrations. Today I spent a good part of the afternoon writing some Silverlight test cases and found myself repeating a stack of awkward, ugly code.

Most of the example code I’ve seen around the web is pretty simplistic and doesn’t deal with DomainContext classes that make multiple calls (submits and loads). I’m sure this will change as ria services mature, but at the moment rich examples are hard to come by.

The issue I was having was tracking which call was returning in two of the main events that a DomainContext raises: Loaded and Submitted. These  events are useful in the sense that they tell you when your call has completed, but if you’ve made multiple calls simultaneously then it’s not easy to tell which call is the one that has raised the event. For instance, when the Loaded event is raised, the LoadedDataEventArgs gives you access to a LoadedEntities property which holds the collection of newly loaded entities. You could then check the type of the first entity in order to see which load call has completed, but this is not a great solution as a call may have returned null (i.e. zero results)

Enter UserState. I actually couldn’t find much documentation on this one, but in a nutshell, it is a parameter of type object that you can pass into load and submit methods of your DomainContext and get back in the LoadedDataEventArgs and SubmittedChangesEventArgs.

On the most simple level you could call a method, say, LoadById() and pass in an int, say 1. When the Loaded event is raised after the call, you get back the LoadedDataEventArgs which now carries a property called UserState which is an int with a value of 1. Nice!

After reading around a bit on the Silverlight forums, I decided on the following approach to streamline things further.

Firstly, I created a delegate in the presenter of my view.

private delegate void CompletedDelegate(EventArgs args);

 

Secondly, I hooked up a reusable method to the Loaded and Submitted events of my DomainContext. The idea is to take the events’ EventArgs and have a look if they have a UserState property that is the delegate type we are after. Here’s the method:

private void contextEventHandler(EventArgs args)
{
    if (args is LoadedDataEventArgs)
    {
        if (((LoadedDataEventArgs)args).UserState is CompletedDelegate)
        {
            ((CompletedDelegate)((LoadedDataEventArgs)args).UserState).Invoke(args);
        }
    }
    else if (args is SubmittedChangesEventArgs)
    {
        if (((SubmittedChangesEventArgs)args).UserState is CompletedDelegate)
        {
            ((CompletedDelegate)((SubmittedChangesEventArgs)args).UserState).Invoke(args);
        }
    }
}

And here’s the constructor of the presenter where I hooked up the method and handled events generated by the view:

public HomePagePresenter(HomePage view)
{
    this.view = view;

    catCtxt = new CategoryDomainContext();
    catCtxt.Loaded += (s, e) => contextEventHandler(e);
    catCtxt.Submitted += (s, e) => contextEventHandler(e);

    view.Click += (s, e) =>
        {
            if ((s as Button).Tag.ToString() == "Save")
            {
                catCtxt.SubmitChanges(new CompletedDelegate(dataSubmitted));
            }
            else if ((s as Button).Tag.ToString() == "Reject")
            {
                catCtxt.RejectChanges();
            }
        };

    loadData();
}

The key part of the above is catCtxt.SubmitChanges(new CompletedDelegate(dataSubmitted)); which sets the UserState as the delegate. Now, when the call to submit completes, the method dataSubmitted will be called. Here is that method:

private void dataSubmitted(EventArgs e)
{
    SubmittedChangesEventArgs args = e as SubmittedChangesEventArgs;
    if (args.Error != null)
    {
        HtmlPage.Window.Alert(args.Error.Message);
    }
    else
    {
        HtmlPage.Window.Alert("Data submission complete");
    }
}

Finally, it’s worth taking a look at the loadData() method which again passes in a delegate as UserState:

private void loadData()
{
    catCtxt.LoadCategories(null, new CompletedDelegate(dataLoaded));
}

And the dataLoaded method:

private void dataLoaded(EventArgs e)
{
    notifyChange("Categories");
}

And to road off the code bits, here’s the simplistic DomainService class I used in the example:

[EnableClientAccess()]
public class CategoryDomainService : DomainService
{
    public IQueryable<Category> GetCategories()
    {
        return CategoryHelper.GetCategories().AsQueryable();
    }

    public void InsertCategory(Category cat)
    {
    }

    public void UpdateCategory(Category newCat, Category originalCat)
    {
    }

    public void DeleteCategory(Category cat)
    {
    }
}

All in all I was pretty happy with this technique. It seemed to make ria services a little more WCF-like to me, while still retaining all the major benefits of ria services. The code is still pretty rough, but hey – all my test cases are now passing and the code is a lot more readable than it was. Happy days!

NOTE: It’s probably worth pointing out that the July CTP of ria services may well render this example obsolete when it comes out. soon i hope!!!

 

Get the source

Categories: Work

The realities of blogging

by Tom 30. June 2009 10:15

So far my blogging experiment has been a bit lame. I’d envisioned doing a little better than 5 posts in 7 months. I actually decided to spend a chunk of time catching up last weekend, but then I became sidetracked updating my blog engine and finding a new theme. 3 hours later it was bed time and I still hadn’t written a post. It’s not that I’ve got nothing to say – there are a million things I could post about right now – it’s just hard to find the time. We’re staring down the barrel of a huge deadline at work, and we’ve retained the services of a team of Chinese blokes  who’ll either make or break the project. It’s scary to feel like you're losing control. Be that as it may, my mission is to write more posts, and I think a good starting point is Prism, which has been occupying a lot of my time recently. Time will tell…

Tags:
Categories: Random

Building a .NET RIA service project as a Silverlight class library

by Tom 30. June 2009 09:56

I’ve spent a lot of time over the past few days playing with the new .NET RIA services which can be installed alongside Silverlight 3. RIA services are great, and one thing I’m really liking is how much old code I’ll be able to throw away (like stacks of custom WCF wrappers on the Silverlight side to manage async state and batching multiple service requests).

The original pressure for my team to adopt RIA services was mainly driven by the nice end-to-end data validation framework that it ships with. We started rolling our own and it quickly became clear how much work it was going to take, and – let’s be fair – nobody likes writing that sort of code. If you haven’t checked it out already, a good place to start is over at Brad Abrams’ blog.

A key goal of RIA services is to simplify n-tier architectures, and it does this by creating a link between a ASP.NET web application project and a Silverlight application project and generating a lot of code that is magically shared between the two. I won’t go into this in any detail, because it’s not the point of this post.

One of the big issues I hit first up is this: once you install the RIA services pack, you are given the ability to link a ASP.NET web application project with a Silverlight application, but not a Silverlight class library. My particular usage scenario means I need to share a service DLL  amongst a bunch of XAPs (i.e. modules of a Prism solution) , so it’s no good for me to have a direct link between the RIA services web app and a Silverlight app (e.g. the Prism shell). I need a library I can share between all my modules.

When you create a new web application project, you get the following option:

1

Alternatively, you can go into the properties of a Silverlight application and link the server project there:

2

But strangely enough, these options don’t exist when you are dealing with a Silverlight class library.

The work around is this: create you ASP.NET web application and then create your Silverlight class library. The two aren’t linked yet. Exit visual studio and open the .csproj file of the class library project. You want to add the following:

<PropertyGroup>
	....
	<LinkedServerProject>..\SilverlightApplication1.Web\SilverlightApplication1.Web.csproj</LinkedServerProject>
	...
</PropertyGroup>

Just make sure the path points to your web application project.

Now when you re-open visual studio and build your web application project (assuming it has some domain service classes in it) you’ll see the generated code in your Silverlight class library.

I should probably note that this will all probably change by the time the July CTP comes out (Brad Abrams implied as much in a twitter response to my question).

Categories: Work