tags:

views:

532

answers:

3

So I ran into this problem trying to implement MVVM. AFAIK the best way to execute a method in the ViewModel class is through a CommandBinding.

<Button Command={Binding DoSomethingCommand} />

Only this time I need to do something on a ListBoxItem double click, and the ListBoxItem doesn't implement ICommandSource. So I'm wondering what's the best approach to do this, if there is one.

Thanks!

Edit:

I just thought of a way, but it seems rather hacky. What if I expose the ListBox.DoubleClick event, and my ViewModel class subscribes to it and runs the correct method when the DoubleClick is fired?

+2  A: 

You can used attached behaviors.

See here: http://geekswithblogs.net/HouseOfBilz/archive/2009/05/29/adventures-in-mvvm-ndash-commanding-with-list-boxes.aspx

NerdFury
The example seems a little extensive, I'll give a shot when I get home and I'll give feedback. Thanks for the suggestion!
Carlo
A: 

Silverlight doesn't contain a Command button like the Button in WPF. The way we get around it there is to create a custom control that contains a command and maps that event to the command. Something like this should work.

public class CommandListBoxItem : ListBoxItem
{
    public CommandListBoxItem()
    {
        DoubleClick += (sender, e) =>
        {
            if (Command != null && Command.CanExecute(CommandParameter))
                Command.Execute(CommandParameter);
        };
    }

    #region Bindable Command Properties

    public static DependencyProperty DoubleClickCommandProperty =
        DependencyProperty.Register("DoubleClickCommand",
                                    typeof(ICommand), typeof(CommandListBoxItem),
                                    new PropertyMetadata(null, DoubleClickCommandChanged));

    private static void DoubleClickCommandChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
    {
        var item = source as CommandListBoxItem;
        if (item == null) return;

        item.RegisterCommand(args.OldValue as ICommand, args.NewValue as ICommand);
    }

    public ICommand DoubleClickCommand
    {
        get { return GetValue(DoubleClickCommandProperty) as ICommand; }
        set { SetValue(DoubleClickCommandProperty, value); }
    }

    public static DependencyProperty DoubleClickCommandParameterProperty =
        DependencyProperty.Register("DoubleClickCommandParameter",
                                    typeof(object), typeof(CommandListBoxItem),
                                    new PropertyMetadata(null));

    public object DoubleClickCommandParameter
    {
        get { return GetValue(DoubleClickCommandParameterProperty); }
        set { SetValue(DoubleClickCommandParameterProperty, value); }
    } 

    #endregion

    private void RegisterCommand(ICommand oldCommand, ICommand newCommand)
    {
        if (oldCommand != null)
            oldCommand.CanExecuteChanged -= HandleCanExecuteChanged;

        if (newCommand != null)
            newCommand.CanExecuteChanged += HandleCanExecuteChanged;

        HandleCanExecuteChanged(newCommand, EventArgs.Empty);
    }

    private void HandleCanExecuteChanged(object sender, EventArgs args)
    {
        if (DoubleClickCommand != null)
            IsEnabled = DoubleClickCommand.CanExecute(DoubleClickCommandParameter);
    }      
}

Then when you create your ListBoxItems you bind to the new Command Property.

<local:CommandListBoxItem DoubleClickCommand="{Binding ItemDoubleClickedCommand}" />
bendewey
+2  A: 

You could handle the event in the code-behind file and call the method on the ViewModel object. In my opinion this is a lot better than starting to hack. :-) I won’t pass a WPF routed event to a ViewModel object.

Who says that code-behind is forbidden? The Model-View-ViewModel pattern definitely not.

jbe