# Tuesday, 10 March 2009

I’m building a MVVM app in WPF, and needed to show a modal dialog, but I couldn’t figure out how to make it work and still maintain the separation between ViewModels and Views.  I have been mapping Views to ViewModels using DataTemplates, so that the relationship is declarative in XAML rather than in code someplace.  What I ended up with was a new Window called Dialog that takes a type in its constructor that corresponds to a ViewModelBase-derived type.  (See Josh Smith’s WPF Apps With The Model-View-ViewModel Design Pattern article for background on how the pieces fit together…) 

public partial class Dialog : Window
{
    public Dialog(Type vmType)
    {
        InitializeComponent();

        ViewModelBase vmb = Activator.CreateInstance(vmType) as ViewModelBase;
        
        item.Content = vmb;

        this.Title = vmb.DisplayName;
    }
}

In the XAML for Dialog, there’s just a Grid that contains a ContentPresenter called “item”.  The constructor sets item’s content to be the ViewModel, and the DataTemplate takes care of associating the View (a UserControl) with the ViewModel.  Note the use of SizeToContent="WidthAndHeight" on the Dialog window, which causes the window to resize to how ever big the UserControl that represents the View might be.

<Window x:Class="SomeNamespace.Dialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Dialog" SizeToContent="WidthAndHeight">
    <Grid>
        <ContentPresenter x:Name="item">
            
        </ContentPresenter>
    </Grid>
</Window>
<DataTemplate DataType="{x:Type vm:NewThingViewModel}">
    <vw:NewThing/>
</DataTemplate>

To create an instance of the new modal dialog, I just create a new instance of Dialog and pass the type of ViewModel it’s supposed to host…

new Dialog(typeof(NewThingViewModel)).ShowDialog();

There are still some details to work out as far as getting results from said dialog, but hey, progress…

Tuesday, 10 March 2009 15:36:01 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 04 March 2009

So yesterday I was reading Josh Smith’s WPF Apps With The Model-View-ViewModel Design Pattern article in last month’s MSDN magazine, and I really liked the sample app that he built.  It totally cleared up for me some rough edges (in my understanding) of MVVM.  Most specifically, I had been conflicted about where to put things like button click handlers.  Do you put a Click event handler in your codebehind, which just defers to the View Model?  Do you create a generic CommandBinding handler to wire up commands to your View Model’s methods?  (I’ve seen both in various examples…)  The former seems like cheating a bit on MVVM (no code behind would be ideal, it seems to me) and the latter overly complicated.  Josh’s solution is to bind the Command property of the UIElement (button, menu item, whatever) to a property of type ICommand on the View Model. 

That totally made sense to me.  No more code behind (like at all, basically) and I don’t have to build additional framework to make it happen, with the exception of coming up with an ICommand implementation.  Josh solved that with a class he calls RelayCommand, which just wraps an ICommand implementation around a delegate (or lambda) of type Action<object>, with an optional Predicate<object> to handle the CanExecute method of ICommand. 

Groovy, now my XAML knows absolutely nothing about the View Model, and it’s only links to said View Model are through data binding.

Then I got to wondering if something as easy could work in Silverlight…  The answer turned out to be almost, but not quite.  Since Silverlight controls don’t expose a Command property, you have to hook them up yourself, but otherwise it works just the same way.

So if I’ve got a page with a single button on it

CropperCapture[10]

<UserControl x:Class="SilverlightCommands.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:my="clr-namespace:SilverlightCommands"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Say Hello..." VerticalAlignment="Center" 
                HorizontalAlignment="Center" 
                my:ButtonService.Command="{Binding Path=SayHello}"/>
    </Grid>
</UserControl>

I can add an attached property whose value is bound to the ICommand property on the View Model.  The attached property grabs the ICommand and hooks up the button’s click handler to the ICommand’s Execute method.

#region Command

/// <summary>
/// Command Attached Dependency Property
/// </summary>
public static readonly DependencyProperty CommandProperty =
    DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(ButtonService),
        new PropertyMetadata(OnCommandChanged));

/// <summary>
/// Gets the Command property.
/// </summary>
public static ICommand GetCommand(DependencyObject d)
{
    return (ICommand)d.GetValue(CommandProperty);
}

/// <summary>
/// Sets the Command property.
/// </summary>
public static void SetCommand(DependencyObject d, ICommand value)
{
    d.SetValue(CommandProperty, value);
}

/// <summary>
/// Handles changes to the Command property.
/// </summary>
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is Button)
    {
        Button b = d as Button;
        ICommand c = e.NewValue as ICommand;
        b.Click += delegate(object sender, RoutedEventArgs arg) { c.Execute(null); };
    }
}

#endregion

In the View Model, then, is the actual handler for the command

public class PageViewModel
{
    public ICommand SayHello
    {
        get
        {
            return new RelayCommand(param => MessageBox.Show("HelloWorld"));
        }
    }
}

Note that I’m using Josh’s RelayCommand helper to wrap the simple lambda.

That feels like not too terribly much infrastructure, although I might have to create separate attached properties to handle different control types (e.g. those that don’t have a Click event).  It would be pretty straightforward to support command parameters in the same way, by creating a CommandParameter attached property and picking up its value in the OnCommandChanged handler…

private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is Button)
    {
        string parameter = d.GetValue(CommandParameterProperty) as string;
        Button b = d as Button;
        ICommand c = e.NewValue as ICommand;
        b.Click += delegate(object sender, RoutedEventArgs arg) { c.Execute(parameter); };
    }
}

The only thing that doesn’t really handle well is the CanExecute method, which in WPF would automatically enable/disable the button based on the result of the ICommand’s CanExecute method.  I tried a couple ways of wiring it up during the OnCommandChanged handler, but couldn’t come up with anything that didn’t look to have nasty side-effect (garbage collection, etc.) or be just ugly.  I’d probably just bind the IsEnable property of the button to a separate boolean on the View Model and deal with it there. 

Update:

Code is here.

Wednesday, 04 March 2009 16:06:59 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [3]  |