views:

117

answers:

1

Hello

I have the same command that I want to use for two controls on a dialog type window. As potentially interesting background, I'm using Josh Smith's ViewModel / RelayCommand ideas, since I am new to WPF and it's the first thing I've seen that I can actually understand from a big picture point of view.

So the command is a property of a ViewModel, and with the Button's built-in support, it is trivial and painless to bind to the command in the XAML:

<Button ... Command="{Binding Path=PickCommand}"  Content="_Ok"></Button>

Now in a ListView, the only way I have gotten to use the same command hooked up to trigger on a double click is by using an event handler:

<ListView ...
              ItemsSource="{Binding Path=AvailableProjects}" 
              SelectedItem="{Binding Path=SelectedProject, Mode=TwoWay}"
              MouseDoubleClick="OnProjectListingMouseDoubleClick"
              >

private void OnProjectListingMouseDoubleClick(object sender, MouseButtonEventArgs e) {
        var vm = (ProjectSelectionViewModel) DataContext;
        vm.Pick(); // execute the pick command
    }

Is there a way to do this by binding the way the button does it?

Cheers,
Berryl

<------- implementation - is there a better way? --->

Your SelctionBehavior class was spot on, but I was confused at your xaml code. By setting the "Style" on the listViewItem I was getting the children of the DataContext where the command I want to execute lives. So I attached the behavior to the ListView itself:

<ListView ...Style="{StaticResource _attachedPickCommand}" >

And put the style in a resource dictionary:

<Style x:Key="_attachedPickCommand" TargetType="ListView">
    <Setter Property="behaviors:SelectionBehavior.DoubleClickCommand" Value="{Binding Path=PickCommand}" />
</Style>

It works! But it 'feels' awkward setting the style property of the list view. Is this just because I am not comfortable with style as more than something visual in wpf or is there a better way to do this?

Cheers, and thanks!
Berryl

+1  A: 

Yes there is! You can use attached behaviors and bind the command to that behavior.

  public class SelectionBehavior {
public static readonly DependencyProperty CommandParameterProperty=
  DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(SelectionBehavior));

public static readonly DependencyProperty DoubleClickCommandProperty=
  DependencyProperty.RegisterAttached("DoubleClickCommand", typeof(ICommand), typeof(SelectionBehavior),
                                      new PropertyMetadata(OnDoubleClickAttached));

private static void OnDoubleClickAttached(DependencyObject d, DependencyPropertyChangedEventArgs e) {
  var fe=(FrameworkElement)d;

  if(e.NewValue!=null && e.OldValue==null) {
    fe.PreviewMouseDown+=fe_MouseDown;
  } else if(e.NewValue==null && e.OldValue!=null) {
    fe.PreviewMouseDown-=fe_MouseDown;
  }
}

private static void fe_MouseDown(object sender, MouseButtonEventArgs e) {
  if(e.ClickCount==2) {
    var dep=(FrameworkElement)sender;

    var command=GetDoubleClickCommand(dep);

    if(command!=null) {
      var param=GetCommandParameter(dep);
      command.Execute(param);
    }
  }
}

public static ICommand GetDoubleClickCommand(FrameworkElement element) {
  return (ICommand)element.GetValue(DoubleClickCommandProperty);
}

public static void SetDoubleClickCommand(FrameworkElement element, ICommand value) {
  element.SetValue(DoubleClickCommandProperty, value);
}

public static object GetCommandParameter(DependencyObject element) {
  return element.GetValue(CommandParameterProperty);
}

public static void SetCommandParameter(DependencyObject element, object value) {
  element.SetValue(CommandParameterProperty, value);
}

}

and in the xaml you would need to set a style for a ListViewItem which represents your data in the ListView. Example

        <ListView>
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="local:SelectionBehavior.DoubleClickCommand" Value="{Binding Path=DataContext.PickCommand}"/>
                <Setter Property="local:SelectionBehavior.CommandParameter" Value="{Binding Path=DataContext}"/>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>

Here is some more information about the Attached Behavior pattern

Pavel
Hi Pavel. I can't grok all of this right now, but is it reuseable? Looks like a fair bit of setup if not. Josh Smith again too - looks like this guy is a good one to stick with for a bit. Certainly got lots of material out there. Thks!
Berryl
Berryl, yes it is certainly reusable. You can set this behavior on any FrameworkElement and attach a command to it. For example you can create an image element and attach the double click behavior to it. <Image Source="..." local:SelectionBehavior.DoubleClickCommand="{Binding Path=PickImageCommand}"/>. Now whenever the image is double clicked, the PickImageCommand will execute. Optionally you can also add a command parameter the same way.
Pavel
Hi Pavel. Can you take a look at my implementation of your (working) idea and comment? Thanks!
Berryl
It's at the bottom of my edited question above...
Berryl
Berryl, I'm glad to hear that it worked for you. You can set the attached command on the listview directly. <ListView behaviors:SelectionBehavior.DoubleClickCommand="{Binding Path=PickCommand}" .../> and it should work just fine. It helps putting it in a style if you have multiple ListView's that want to use the same properties but you don't have to. Hope that helped!
Pavel
Magic! Thanks again...
Berryl