1

Currently I am trying to display a ObservableCollection of an custom class in a TreeView, when the user double clicks on a 'item' it will fire an method in the ViewModel passing the selected custom class as parameter. I am using the MVVM structure for my WPF Application.

The problem I am facing with this is that the Observable Collection is displayed with an HierarchicalDataTemplate. See underneath the whole XAML code for the TreeView

<TreeView Name="DeviceTreeView" ItemsSource="{Binding ViewableTIADeviceTree}" Grid.Column="3" Margin="5">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type treeviewable:ViewableTIADevice}" ItemsSource="{Binding DeviceItems}">
                        <TextBlock Text="{Binding Path=DeviceName}"/>

                        <HierarchicalDataTemplate.ItemContainerStyle>
                            <Style TargetType="{x:Type treeviewable:ViewableTIADevice}">
                                <Setter Property="commandBehaviors:MouseDoubleClick.Command"
                                    Value="{Binding TIADeviceTreeItemDoubleClick}"/>
                                <Setter Property="commandBehaviors:MouseDoubleClick.CommandParameter"
                                    Value="{Binding}"/>
                            </Style>
                        </HierarchicalDataTemplate.ItemContainerStyle>
                    </HierarchicalDataTemplate>
                    <DataTemplate DataType="{x:Type treeviewable:ViewableDeviceItem}">
                        <TextBlock Text="{Binding Path=Name}"/>
                    </DataTemplate>
                </TreeView.Resources>
            </TreeView>

And the MouseDoubleClick attached behavior class:

public class MouseDoubleClick
{
    public static DependencyProperty CommandProperty = 
        DependencyProperty.RegisterAttached("Command", 
            typeof(ICommand), 
            typeof(MouseDoubleClick), 
            new UIPropertyMetadata(CommandChanged));

    public static DependencyProperty CommandParameterProperty = 
        DependencyProperty.RegisterAttached("CommandParameter",
        typeof(object),
        typeof(MouseDoubleClick),
        new UIPropertyMetadata(null));

    public static void SetCommand(DependencyObject target, ICommand value)
    {
        target.SetValue(CommandProperty, value);
    }

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

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

    public static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    {
        Control control = target as Control;

        if(control != null)
        {
            if((args.NewValue != null) && (args.OldValue == null))
            {
                control.MouseDoubleClick += OnMouseDoubleClick;
            }
            else if((args.NewValue == null) && (args.OldValue != null))
            {
                control.MouseDoubleClick -= OnMouseDoubleClick;
            }
        }
    }

    private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
    {
        Control control = sender as Control;
        ICommand command = (ICommand)control.GetValue(CommandProperty);

        object commandParameter = control.GetValue(CommandParameterProperty);
        command.Execute(commandParameter);
    }

}

The problem I am facing with this is that it says that the 'ViewableTIADevice' is not an FrameWorkElement and thus I cannot even run it.

I've also tried using the

<Style TargetType"{x:Type TreeViewItem}">

That does run but I get no response when trying to double click an item in the TreeView.

I've searched a lot for the solution and I would like to refer to this thread: WPF/MVVM - how to handle double-click on TreeViewItems in the ViewModel?

I've been using the above thread as solution but how can I combine that solution with an HierarchicalDatatemplate?

EDIT

The ICommand that I am trying to call by double clicking an item

 public RelayCommand TIADeviceTreeItemDoubleClick { get; set; }

Where I am here assigning it to the function

 TIADeviceTreeItemDoubleClick = new RelayCommand(c => tiaDeviceTreeItemDoubleClick(c));

And the function it refers to:

 private void tiaDeviceTreeItemDoubleClick(object value)
    {
        //code
    }

This is the ViewableTIADevice class:

public class ViewableTIADevice
{
    public ViewableTIADevice()
    {
        DeviceItems = new List<ViewableDeviceItem>();
    }
    public string DeviceName { get; set; }

    public IList<ViewableDeviceItem> DeviceItems { get; set; }
}
R Bakker
  • 27
  • 5
  • show us where you define the ICommand "TIADeviceTreeItemDoubleClick", because i fear you are not defining it in the class that is inside your collection DeviceItems – Milan Feb 15 '18 at 14:04
  • well, then try setting the command like this: Value="{Binding ElementName=DeviceTreeView, Path=DataContext.TIADeviceTreeItemDoubleClick}" – Milan Feb 15 '18 at 14:08
  • That does not work, the exception that keeps being thrown is 'Configuring the property of System.Windows.Style.TargetType caused an exception on line 52 position 34' Line 52 is: – R Bakker Feb 15 '18 at 14:15
  • does ViewableTIADevice derive from TreeViewItem? try " – Milan Feb 15 '18 at 14:16
  • No it doesn't I've tried doing that and overriding the "GetContainerForItemOverride" and "IsItemItsOwnContainerOverride". But the tree is empty. – R Bakker Feb 15 '18 at 14:18
  • Well, suddenly it works now. By changing the first setter's value to Value="{Binding ElementName=DeviceTreeView, Path=DataContext.TIADeviceTreeItemDoubleClick}". How could this fix the whole problem? By having the value of {Binding TIADeviceTreeItemDoubleClick} it should work because it's already bound to the ViewModel? – R Bakker Feb 15 '18 at 14:24
  • ill try to write a simple explanation in a few mins – Milan Feb 15 '18 at 14:25

1 Answers1

0

i believe you have a missunderstanding of what your datacontext is, consider this example:

<TreeView ItemsSource="{Binding Items}">
        <TreeView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding ItemProperty1}"></TextBlock>
            </DataTemplate>
        </TreeView.ItemTemplate>
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="Local:MouseDoubleClick.Command"
                                Value="{Binding ElementName=DeviceTreeView, Path=DataContext.TIADeviceTreeItemDoubleClick}"/>
                <Setter Property="Local:MouseDoubleClick.CommandParameter"
                                Value="{Binding}"/>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

the datacontext of my TreeView, is my viewmodel, so when i say ItemsSource="{Binding Items}", i am binding to the observable collection called Items inside my ViewModel.

the datacontext inside TreeView.ItemTemplate, is one single item inside the collection Items. meaning that when i say {Binding ItemProperty1}, i am binding, NOT to ViewModel.ItemProprty1, but to a single item inside the collection ViewModel.Items. this means that you have to have a ViewModel, and inside a collection called Items, and inside this collection you need to have objects of type X, and the class X must have a property called ItemProperty1.

the datacontext inside the TreeView.ItemContainerStyle, is also one single item inside the collection Items, meaning that when you say {Binding TIADeviceTreeItemDoubleClick}, you are trying to bind to an ICommand property that is inside of the class that is inside your collection Items. your datacontext here, is not, as you assumed, your ViewModel, but rather one single item inside ViewModel.Items

so when you use this:

Value="{Binding ElementName=DeviceTreeView, Path=DataContext.TIADeviceTreeItemDoubleClick}"

you are binding to the datacontext of the TreeView, which is ViewModel, which contains an ICommand property called TIADeviceTreeItemDoubleClick.

when you write this:

{Binding TIADeviceTreeItemDoubleClick}

you are trying to bind to the datacontext of the current TreeViewItem, which is one single object inside your collection Items. so this will only work if you add the ICommand to your class that is inside your collection Items.

you also use this:

<Setter Property="Local:MouseDoubleClick.CommandParameter"
                                Value="{Binding}"

here, obviosly, you are sending one single item inside your collection Items, and not the ViewModel.

make sense?

Milan
  • 616
  • 5
  • 11
  • 1
    Ahh, yes it does make sense now. I indeed assumed that the datacontext of the ItemContainerStyle's Setter is the ViewModel. Thank you for helping me in such short time! – R Bakker Feb 15 '18 at 14:49