Denys Wessels
Denys Wessels

Reputation: 17039

Handling WPF TreeView mouse double click using MVVM

I've created a Dependency Property to handle a mouse double click event of a tree view. It's partially working in that when you double click the tree view item the required method is called in my view model, now what I would like to do is pass through the clicked item(XmlElement) to this method.I've added another Dependency property for the parameter but everytime I double click a tree view item the XmlElement parameter in the method is null.

There are a number of external libraries such as :

1) Expression.Sample.Interactivity and

2) Attached Command Behavior

but I've been asked to implement something myself and I would really appreciate if someone could advice where am I going wrong.I've included the necessary source below:

Class which handles the dependency properties:

  public static class CommandBehavior
  {

  public static readonly DependencyProperty DoubleClickCommand =
  EventBehaviourFactory.CreateCommandExecutionEventBehaviour(
  TreeView.MouseDoubleClickEvent,
  "DoubleClickCommand",
 typeof(CommandBehavior));

public static void SetDoubleClickCommand(DependencyObject o, ICommand value)
{
    o.SetValue(DoubleClickCommand, value);
}

public static ICommand GetDoubleClickCommand(DependencyObject o)
{
    return o.GetValue(DoubleClickCommand) as ICommand;
}

public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", 
typeof(object),
typeof(CommandBehavior),
new FrameworkPropertyMetadata((object)null,
    new PropertyChangedCallback(OnCommandParameterChanged)));

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

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

private static void OnCommandParameterChanged(DependencyObject d,  DependencyPropertyChangedEventArgs e)
{
}

}

 public static class EventBehaviourFactory
 {
 public static DependencyProperty CreateCommandExecutionEventBehaviour(RoutedEvent routedEvent, string propertyName, Type ownerType)
 {
    DependencyProperty property = DependencyProperty.RegisterAttached(propertyName, typeof(ICommand),
        ownerType, new PropertyMetadata(null,
            new ExecuteCommandOnRoutedEventBehaviour(routedEvent).PropertyChangedHandler));
    return property;
}

}

public class ExecuteCommandOnRoutedEventBehaviour : ExecuteCommandBehaviour
{
   private readonly RoutedEvent _routedEvent;
   public ExecuteCommandOnRoutedEventBehaviour(RoutedEvent routedEvent)
   {
      _routedEvent = routedEvent;
   }

 protected override void AdjustEventHandlers(DependencyObject sender, object oldValue,   object newValue)
 {
    UIElement element = sender as UIElement;
    if (element == null)
        return;

    if (oldValue != null)
    {
        element.RemoveHandler(_routedEvent, new RoutedEventHandler(EventHandler));
    }
    if (newValue != null)
    {
        element.AddHandler(_routedEvent, new RoutedEventHandler(EventHandler));
    }
}

protected void EventHandler(object sender, RoutedEventArgs e)
{
    HandleEvent(sender, e);
}

}

public abstract class ExecuteCommandBehaviour
{
protected DependencyProperty _property;
protected abstract void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue);
protected void HandleEvent(object sender, EventArgs e)
{
    DependencyObject dp = sender as DependencyObject;
    if (dp == null)
        return;

    DelegateCommand<XmlElement> command = dp.GetValue(_property) as DelegateCommand<XmlElement>;
    if (command == null)
    {
        return;
    }
    command.Execute(sender as XmlElement);
}

public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    if (_property == null)
        _property = e.Property;

    object oldValue = e.OldValue;
    object newValue = e.NewValue;
    AdjustEventHandlers(sender, oldValue, newValue);
}

}

Relevant XAML:

<TreeView Name="treeView1"
          local:CommandBehavior.DoubleClickCommand="{Binding ClickedItem}"
          local:CommandBehavior.CommandParameter="{Binding ElementName=treeView1, Path=SelectedItem}">

View model:

 public partial class FieldTreeViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private DelegateCommand<XmlElement> _clickedItem;
        public DelegateCommand<XmlElement> ClickedItem 
        {
            get { return _clickedItem; }
            set 
            { 
                _clickedItem = value;
                OnPropertyChanged("ClickedItem");
            }
        }

        public FieldTreeViewModel()
        {
            ClickedItem = new DelegateCommand<XmlElement>(InsertContentControl);
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private void InsertContentControl(XmlElement selectedElement)
        {
            //The method always get's executed when tree view item is double clicked but
            //the selectedElement parameter is always null
        }
    }

Upvotes: 1

Views: 3972

Answers (1)

Wolfgang Ziegler
Wolfgang Ziegler

Reputation: 1685

Put a breakpoint on this line

command.Execute(sender as XmlElement);

You will notice that the sender is not of type XmlElement (it's the treeview) and you therefore get null all the time. You have to retrieve the CommandParameter (which is the XmlElement) you are binding to and specify it to the Execute call.

Upvotes: 5

Related Questions