How to get TreeViewItem from HierarchicalDataTemplate item?

前端 未结 11 1396
失恋的感觉
失恋的感觉 2020-12-02 11:34

I have a TreeView which uses a HierarchicalDataTemplate to bind its data.

It looks like this:



        
相关标签:
11条回答
  • 2020-12-02 12:03

    I modified William's recursive search to a more compact version:

    public TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object targetObject) {
        if (container.ContainerFromItem(targetObject) is TreeViewItem target) return target;
        for (int i = 0; i < container.Items.Count; i++)
            if ((container.ContainerFromIndex(i) as TreeViewItem)?.ItemContainerGenerator is ItemContainerGenerator childContainer)
                if (GetTreeViewItemFromObject(childContainer, targetObject) is TreeViewItem childTarget) return childTarget;
        return null;
    }
    

    One would call it by providing the TreeView instance's ItemContainerGenerator and the target data object:

    TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);
    
    0 讨论(0)
  • 2020-12-02 12:06

    Here is solution. rtvEsa is treeview. HierarchicalDataTemplate is treeview template Tag make real consumation of current item. This is not selected item it is current item in tree control that use HierarchicalDataTemplate.

    Items.CurrentItem is part of internal tree colection. You cant get many and diferent data. For example Items.ParenItem too.

      <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">
    
      <TextBox Tag="{Binding ElementName=rtvEsa, Path=Items.CurrentItem }" />
    

    0 讨论(0)
  • 2020-12-02 12:07

    If you have to find item in children of children you may have to use recursion like this

    public bool Select(TreeViewItem item, object select) // recursive function to set item selection in treeview
    {
        if (item == null)
            return false;
        TreeViewItem child = item.ItemContainerGenerator.ContainerFromItem(select) as TreeViewItem;
        if (child != null)
        {
            child.IsSelected = true;
            return true;
        }
        foreach (object c in item.Items)
        {
            bool result = Select(item.ItemContainerGenerator.ContainerFromItem(c) as TreeViewItem, select);
            if (result == true)
                return true;
        }
        return false;
    }
    
    0 讨论(0)
  • 2020-12-02 12:11

    Do you need the TreeViewItem because you're going to modify what is being displayed? If that is the case, I'd recommend using a Style to change how the item is being displayed instead of using code-behind instead of directly modifying the TreeViewItem. It should hopefully be cleaner.

    0 讨论(0)
  • 2020-12-02 12:12

    Inspired by Fëanor’s answer, I’ve attempted to make the TreeViewItem easily accessible for every data item that a TreeViewItem has been created for.

    The idea is to add a TreeViewItem-typed field to the view model, also exposed via an interface, and have the TreeView automatically populate it whenever the TreeViewItem container is created.

    This is done by subclassing TreeView and attaching an event to the ItemContainerGenerator, which records the TreeViewItem whenever it’s created. Gotchas include the fact that TreeViewItems are created lazily, so there might genuinely not be one available at certain times.

    Since posting this answer, I've developed this further and used it for a long time in one project. No issues so far, other than the fact that this violates MVVM (but also saves you a ton of boilerplate for the simple cases). Source here.

    Usage

    Select the parent of the selected item and collapse it, ensuring that it’s in the view:

    ...
    var selected = myTreeView.SelectedItem as MyItem;
    selected.Parent.TreeViewItem.IsSelected = true;
    selected.Parent.TreeViewItem.IsExpanded = false;
    selected.Parent.TreeViewItem.BringIntoView();
    ...
    

    Declarations:

    <Window ...
            xmlns:tvi="clr-namespace:TreeViewItems"
            ...>
        ...
        <tvi:TreeViewWithItem x:Name="myTreeView">
            <HierarchicalDataTemplate DataType = "{x:Type src:MyItem}"
                                      ItemsSource = "{Binding Children}">
                <TextBlock Text="{Binding Path=Name}"/>
            </HierarchicalDataTemplate>
        </tvi:TreeViewWithItem>
        ...
    </Window>
    
    class MyItem : IHasTreeViewItem
    {
        public string Name { get; set; }
        public ObservableCollection<MyItem> Children { get; set; }
        public MyItem Parent;
        public TreeViewItem TreeViewItem { get; set; }
        ...
    }
    

    Code

    public class TreeViewWithItem : TreeView
    {
        public TreeViewWithItem()
        {
            ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
        }
    
        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            var generator = sender as ItemContainerGenerator;
            if (generator.Status == GeneratorStatus.ContainersGenerated)
            {
                int i = 0;
                while (true)
                {
                    var container = generator.ContainerFromIndex(i);
                    if (container == null)
                        break;
    
                    var tvi = container as TreeViewItem;
                    if (tvi != null)
                        tvi.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
    
                    var item = generator.ItemFromContainer(container) as IHasTreeViewItem;
                    if (item != null)
                        item.TreeViewItem = tvi;
    
                    i++;
                }
            }
        }
    }
    
    interface IHasTreeViewItem
    {
        TreeViewItem TreeViewItem { get; set; }
    }
    
    0 讨论(0)
提交回复
热议问题