locked
How to make a TreeView custom ItemsSource behave like normal TreeViewItems? RRS feed

  • Question

  • Hi,

    I have made a custom class to populate a TreeView thru its ItemsSource. That class heritates ObservableCollection.

    The thing is that its nodes doesnt seem to be TreeViewItems because i cant apply code that works for normal treeviewitems.

    I'm talking about code to do right click select, etc.

    Can somebody tell me how can i solve this problem?

    Thx,

    Nuno
    Monday, September 10, 2007 9:59 AM

All replies

  • If you bind the TreeView to an ObservableCollection<Type> you will always get this collection back from treeview if you query items.
    If you wan't to use the treeview item containing your class you could try to use a VisualTreeHelper to find the container you are looking for but this means that you would have to iterate over all items to find out wich schould be selected.

    I think the problem is that the treeview simply has no collection with all items but one with the root nodes. It is simply not aware of all subitems and that's why you can't set the selected item property like you can in the listbox...

    Best regards,
    ITD
    Monday, September 10, 2007 12:33 PM
  • Can you point me to an example on how to do this?

    Thx,

    Nuno
    Monday, September 10, 2007 1:15 PM
  • What are you doing that would require that you interact with the treeview items instead of your data items? If you work with your data the treeview should update itself.

     

     

    also, if you want right click behavior, add a context menu with commands or events.

    Monday, September 10, 2007 2:30 PM
  • What i really want is to be able to automaticly select a node when i right click it, and then open the context menu.

    I can do this perfectly using the left button to select the node and then using the right click to access the context menu.

    The problem is that i cant use directly the right button which is something typical in windows enviorment.

    The code i'm using to achieve that is the following:

    Code Snippet

    tvNetworkExplorer.AddHandler(TreeViewItem.MouseDownEvent, new MouseButtonEventHandler(TvNetworkExplorer_MouseDown));

    private void TvNetworkExplorer_MouseDown(object sender, MouseButtonEventArgs e)
            {
                TreeViewItem item = e.Source as TreeViewItem;
               
                if (e.RightButton == MouseButtonState.Pressed)
                {
                    item.IsSelected = true;
                }
            }



    I think this happens because the items are not tree view items, as stated before, they are items from an observable collection of a custom type.

    How can i make such behaviour to happen?

    Many thx,

    Best regards,

    Nuno
    Monday, September 10, 2007 2:54 PM
  • The problem is that even using the visual tree help i always get the first node.

    I need to show you the big picture. Maybe i have something really small failing:

    XAML:

    <TreeView Width="Auto" Height="Auto" DockPanel.Dock="Top" ClipToBounds="False" SnapsToDevicePixels="False" BorderBrush="{x:Null}" FontWeight="Normal" x:Name="tvNetworkExplorer" Background="{x:Null}" Margin="5,5,5,5" >
                            <TreeView.Resources>
                   
                                <HierarchicalDataTemplate DataType="{x:Type src:Group}" ItemsSource="{Binding Path=Items}">
                                    <TextBlock Text="{Binding Path=Name}"/>
                                </HierarchicalDataTemplate>
                   
                                <HierarchicalDataTemplate DataType="{x:Type src:GroupItem}" ItemsSource="{Binding Path=List}">
                                    <TextBlock Text="{Binding Path=Title}"/>
                                </HierarchicalDataTemplate>
                   
                                <DataTemplate DataType="{x:Type srcStick out tonguelayer}">
                                    <TextBlock Text="{Binding Path=Name}"/>
                                </DataTemplate>
                   
                                <DataTemplate DataType="{x:Type src:Behaviour}">
                                    <TextBlock Text="{Binding Path=Name}"/>
                                </DataTemplate>
                            </TreeView.Resources>
                            <TreeViewItem ItemsSource="{Binding}" Header="Network" />
                        </TreeView>


    Code:


    tvNetworkExplorer.DataContext = networkExplorer;


                // Events
                tvNetworkExplorer.AddHandler(TreeViewItem.MouseDownEvent, new MouseButtonEventHandler(TvNetworkExplorer_MouseDown));
                tvNetworkExplorer.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TvNetworkExplorer_SelectedItemChanged);

            private void TvNetworkExplorer_MouseDown(object sender, MouseButtonEventArgs e)
            {
                //TreeViewItem item = e.Source as TreeViewItem;

                TreeView tv = (TreeView) sender;

                TreeViewItem obj = (TreeViewItem) GetBoundItemFromPoint(tv, e.GetPosition(tv));


                Console.WriteLine(obj.Name);
                //Console.WriteLine(item.ToString());
                //if (e.RightButton == MouseButtonState.Pressed)
                //{
                //    item.IsSelected = true;
                //}
            }

            static private object GetBoundItemFromPoint(TreeView tv, Point point)
            {
                UIElement element = tv.InputHitTest(point) as UIElement;

                while (element != null)
                {
                    if (element == tv)
                        return null;

                    object item = tv.ItemContainerGenerator.ItemFromContainer(element);
                    bool itemFound = !object.ReferenceEquals(item, DependencyProperty.UnsetValue);

                    if (itemFound)
                        return item;
                    else
                        element = VisualTreeHelper.GetParent(element) as UIElement;
                }

                return null;
            }


    The result of the right click is always the first treeviewitem, the one that has "Network" as header in the XAML.

    But this is a contradiction as i can select the nodes in the tree with the left click.

    Help here, please...

    Best regards,

    Nuno


    Monday, September 10, 2007 4:17 PM
  • Hi lpx

     

    Do you mean that you don't know how to get the related TreeViewItem of a tree node? If so, the following example shows how to do this. In the example, we use ItemsControl.ItemContainerGenerator.ContainerFromItem method to get the container(TreeViewItem) of a tree node.

     

    Code Snippet

    private void TreeView_MouseDoubleClick(object sender, MouseButtonEventArgs e)

    {

      if (this.theTreeView.SelectedItem != null)

      {

        Node selectedNode = (Node)this.theTreeView.SelectedItem;

        LinkedList<Node> nodeTree = new LinkedList<Node>();

        nodeTree.AddFirst(selectedNode);

        CreateNodeTree(selectedNode, nodeTree);

        ItemsControl parentControl = this.theTreeView;

        foreach (Node node in nodeTree)

        {

          parentControl = (TreeViewItem)parentControl.ItemContainerGenerator.ContainerFromItem(node);

        }

        TreeViewItem treeItem = (TreeViewItem)parentControl;

        treeItem.Background = new SolidColorBrush(Colors.YellowGreen);

      }

    }

    private void CreateNodeTree(Node node, LinkedList<Node> nodeTree)

    {

      if (node.Parent != null)

      {

        nodeTree.AddBefore(nodeTree.Find(node), node.Parent);

        CreateNodeTree(node.Parent, nodeTree);

      }

    }

     

    <Window.Resources>

      <local:NodeCollection x:Key="Nodes"/>

      <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Nodes}">

        <TextBlock Text="{Binding Name}"/>

      </HierarchicalDataTemplate>

    </Window.Resources>

    <TreeView Name="theTreeView" ItemsSource="{StaticResource Nodes}"

      MouseDoubleClick="TreeView_MouseDoubleClick"/>

     

    public class Node

    {

      private List<Node> nodes = new List<Node>();

      public Node(Node parent, string name)

      {

        this.Parent = parent;

        this.Name = name;

      }

      public Node Parent { get; private set; }

      public string Name { get; set; }

      public List<Node> Nodes { get { return this.nodes; } }

    }

    public class NodeCollection : ObservableCollection<Node> { }

     

     

    Best Regards

    Wei Zhou

    Wednesday, September 12, 2007 5:02 AM
  • Wednesday, September 12, 2007 4:30 PM
  • If you are still trying to use the mouse down handler, you have a few options:

     

    1. If your handler is on the TreeView, use the OriginalSource property in the event arguments and walk up the visual parent chain until you find a TreeViewItem. Then, select it. You can walk the visual parent chain by using System.Windows.Media.VisualTreeHelper.GetParent.

     

    2. You could try registering a class handler for type TreeViewItem and the mouse down event. Then, your handler should only be called when mouse events pass through TreeViewItem elements.

     

    3. You could register a class handler for type TreeViewItem and the context menu opening event.

     

    One thing that might have been confusing you before was that TreeViewItem elements are the container elements that are generated for each of your data items. When you add data items to the TreeView, the Items collection will be populated with your data items. You need to use the ItemContainerGenerator.ContainerFromItem or ContainerFromIndex methods to move between the data items and the element containers. In addition, since the TreeView works with hierarchical data, it can be confusing when you are deeper down in the hierarchy since the nested TreeViewItem become the holder of the ItemContainerGenerator that generates even more nested TreeViewItems.

     

    Most routed events, however, deal only with the elements in the visual tree, and these include elements from data templates. So, when you right-click, you are probably right-clicking on a text element or border that you specified in one of your data templates, not directly on a TreeViewItem. The event, however, will eventually route to a TreeViewItem.

     

    Hope that helps,

    Ben

    Friday, September 21, 2007 9:02 PM
  • I was having the hardest time coming up with a way to just auto-select a node in a TreeView that was assigned to an ItemsSource. This post finally enlightened me, so thank you for that. I had been searching for quite a while...

    I was finally gloriously able to auto-select the first node using this:

    TreeViewItem item = (TreeViewItem)theTreeView.ItemContainerGenerator.ContainerFromItem(tree_DocNodes.Items[0]);
    item.IsSelected = true;

    That seems like a long way to go to simply select a node programmatically! But there you have it. Thanks for clearing that up for me!

    Saturday, September 25, 2010 8:47 AM