Attach "Expanded" command while using HierarchicalDataTemplate in TreeView

Topics: Prism v2 - Silverlight 2, Prism v2 - Silverlight 3
Nov 16, 2009 at 8:26 AM
Edited Nov 16, 2009 at 8:33 AM

Hello,

I have a TreeView using several nested HierarchicalDataTemplates as its ItemTemplate, which finally end in a DataTemplate for the deepest node level. In each node I display an image and some text.

To be able to react when nodes are expanded I have created an "expanded command" which binds to an action in my ViewModel. This event works fine if I manually create a TreeView and populate it with TreeViewItems using static data.

My question I hope somebody might help me with is: How do I attach this command to all items in my TreeView while using HierarchicalDataTemplates? HierarchicalDataTemplates have no "Expanded" event but I still want every item in my TreeView to be able to trigger the expanded event.

My command property is attached like this

 

<controls:TreeViewItem command:Expanded.Command="{Binding ExpandedCommand}"/>

 

Here are some code to further explain my problem.

 

<!-- Deepest Level -->
<DataTemplate x:Key="levelnNode">
<!-- Display stuff -->
</DataTemplate>

...

<!-- First Level -->
<common:HierarchicalDataTemplate x:Key="level1Node" ItemsSource="{Binding Children}" ItemTemplate="{StaticResource levelnNode}">
<!-- Display stuff -->
</common:HierarchicalDataTemplate>

<!-- Root Level -->
<common:HierarchicalDataTemplate x:Key="rootNode" ItemsSource="{Binding Children}" ItemTemplate="{StaticResource level1Node}">
<!-- Display stuff -->
</common:HierarchicalDataTemplate>

<!-- Tree View -->
<controls:TreeView ItemsSource="{Binding Tree}" ItemTemplate="{StaticResource rootNode}" />
How would I go about attaching my command to this set up?
Thankful for any help
Kind Regards,
Exipen

 

Nov 16, 2009 at 8:31 AM

Since some of you might find this thread while looking for ways to catch an expanded command in a mvvm-pattern environment I figured It might be helpful if I also paste the code for the Expanded command since I had trouble finding information about it myself. Hopefully saving someone out there the headache of trying to find how to do this.

 public static class Expanded
 {
        private static readonly DependencyProperty ExpandedCommandBehaviorProperty
            = DependencyProperty.RegisterAttached(
            "ExpandedCommandBehavior",
            typeof(ExpandedCommandBehavior),
            typeof(Expanded),
            null);

        public static readonly DependencyProperty CommandProperty
            = DependencyProperty.RegisterAttached(
            "Command",
            typeof(ICommand),
            typeof(Expanded),
            new PropertyMetadata(OnSetCommandCallback));

        public static readonly DependencyProperty CommandParameterProperty
            = DependencyProperty.RegisterAttached(
           "CommandParameter",
           typeof(object),
           typeof(Expanded),
           new PropertyMetadata(OnSetCommandParameterCallback));

        public static ICommand GetCommand(Control control)
        {
            return control.GetValue(CommandProperty) as ICommand;
        }

        public static void SetCommand(Control control, ICommand command)
        {
            control.SetValue(CommandProperty, command);
        }

        public static void SetCommandParameter(Control control, object parameter)
        {
            control.SetValue(CommandParameterProperty, parameter);
        }

        public static object GetCommandParameter(Control control)
        {
            return control.GetValue(CommandParameterProperty);
        }

        private static void OnSetCommandCallback
            (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Control control = dependencyObject as Control;
            if (control != null)
            {
                ExpandedCommandBehavior behavior = GetOrCreateBehavior(control);
                behavior.Command = e.NewValue as ICommand;
            }
        }

        private static void OnSetCommandParameterCallback
            (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Control control = dependencyObject as Control;
            if (control != null)
            {
                ExpandedCommandBehavior behavior = GetOrCreateBehavior(control);
                behavior.CommandParameter = e.NewValue;
            }
        }

        private static ExpandedCommandBehavior GetOrCreateBehavior(Control control)
        {
            ExpandedCommandBehavior behavior =
                control.GetValue(ExpandedCommandBehaviorProperty) as ExpandedCommandBehavior;
            if (behavior == null)
            {
                behavior = new ExpandedCommandBehavior(control);
                control.SetValue(ExpandedCommandBehaviorProperty, behavior);
            }
            return behavior;
        }
}

public class ExpandedCommandBehavior : CommandBehaviorBase<Control> { public ExpandedCommandBehavior(Control control) : base(control) { ((TreeViewItem)control).Expanded += OnExpanded; } private void OnExpanded(object sender, RoutedEventArgs e) { base.ExecuteCommand(); }
}

Aug 18, 2014 at 9:28 PM
Edited Aug 18, 2014 at 9:29 PM
You did save me some time posting this WPF TreeWiewItem behavior. Thanks.

Just for those who are not into PRISM. You will also need the CommandBehaviorBase class defined, for example, here:
https://github.com/xperiandri/PortablePrism/blob/master/Prism.StoreProfile/Commands/CommandBehaviorBase.cs

...and then you can apply all 3 classes with an XAML like this:
xmlns:behav="clr-namespace:Behaviours"
<TreeView.ItemContainerStyle>
  <Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="behav:Expanded.Command" Value="{Binding DemoExpandCommand}" />
  </Style>
</TreeView.ItemContainerStyle>
...where DemoExpandCommand is an ICommand in the bound viewmodel of the treeview item.
The command is triggered upon expansion of the treeview item node.

Great work - thanks a lot.
Jan 12, 2015 at 2:00 AM
exipen wrote:
Hello, I have a TreeView using several nested HierarchicalDataTemplates as its ItemTemplate, which finally end in a DataTemplate for the deepest node level. In each node I display an image and some text. To be able to react when nodes are expanded I have created an "expanded command" which binds to an action in my ViewModel. This event works fine if I manually create a TreeView and populate it with TreeViewItems using static data. My question I hope somebody might help me with is: How do I attach this command to all items in my TreeView while using HierarchicalDataTemplates? HierarchicalDataTemplates have no "Expanded" event but I still want every item in my TreeView to be able to trigger the expanded event. My command property is attached like this   <controls:TreeViewItem command:Expanded.Command="{Binding ExpandedCommand}"/>   Here are some code to further explain my problem.   <!-- Deepest Level --><DataTemplate x:Key="levelnNode"> <!-- Display stuff --></DataTemplate>...<!-- First Level --><common:HierarchicalDataTemplate x:Key="level1Node" ItemsSource="{Binding Children}" ItemTemplate="{StaticResource levelnNode}"> <!-- Display stuff --></common:HierarchicalDataTemplate><!-- Root Level --><common:HierarchicalDataTemplate x:Key="rootNode" ItemsSource="{Binding Children}" ItemTemplate="{StaticResource level1Node}"> <!-- Display stuff --></common:HierarchicalDataTemplate><!-- Tree View --><controls:TreeView ItemsSource="{Binding Tree}" ItemTemplate="{StaticResource rootNode}" /> How would I go about attaching my command to this set up? Thankful for any help Kind Regards, Exipen  
Hi,I meet the the same problem,Is there anyone know how to solve the problem?thanks
Jan 12, 2015 at 2:30 PM
The answer lies above, either the custom behavior shown a few years ago, or could wire it up with the InvokeCommandAction that is now part of Prism 5. Put that on the root element of your HierarchicalDataTemplate using a RelativeSource binding to point to the parent TreeViewItem as the source, EventTrigger catching the Expanded event, Command property pointing to your ViewModel command handler through a binding, and use the TriggerParameterPath property or CommandParameter property to pass along the context of what node was expanded.