如何从HierarchicalDataTemplate项目中获取TreeViewItem?

问题描述 投票:0回答:12
wpf treeview hierarchicaldatatemplate
12个回答
32
投票
TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

不起作用(对我来说),因为使用 HierarchicalDataTemplate 的树视图中的 mainTreeList.Items.CurrentPosition 将始终为 -1。

也没有,因为使用 HierarchicalDataTemplate 的树视图中的 mainTreeList.Items.CurrentItem 将始终为 null。

TreeViewItem item = (TreeViewItem)mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.Items.CurrentItem);

INSTEAD 我必须在路由的 TreeViewItem.Selected 事件中设置最后选定的 TreeViewItem,该事件会冒泡到树视图(TreeViewItem 本身在设计时不存在,因为我们使用的是 HierarchicalDataTemplate)。

可以在 XAML 中捕获事件,如下所示:

<TreeView TreeViewItem.Selected="TreeViewItemSelected" .../> 

然后可以在事件中设置最后选择的 TreeViewItem,如下所示:

    private void TreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;

        // set the last tree view item selected variable which may be used elsewhere as there is no other way I have found to obtain the TreeViewItem container (may be null)
        this.lastSelectedTreeViewItem = tvi;

        ...
     }

24
投票

来自 Bea Stollnitz 关于此的博客条目,请尝试

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

10
投票

我遇到了同样的问题。 我需要访问 TreeViewItem 以便选择它。 然后我意识到我可以将属性 IsSelected 添加到我的 ViewModel,然后将其绑定到 TreeViewItems IsSelectedProperty。 这可以通过 ItemContainerStyle 来实现:

<TreeView>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
</TreeView>

现在,如果我想选择树视图中的某个项目,我只需直接在 ViewModel 类上调用 IsSelected 即可。

希望它对某人有帮助。


7
投票
TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition)); gives first item in the TreeView because CurrentPosition is always 0.

怎么样

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.SelectedItem)));

这对我来说效果更好。


4
投票

受 Fëanor 答案的启发,我尝试让已创建

TreeViewItem
的每个数据项都可以轻松访问
TreeViewItem

这个想法是向视图模型添加一个

TreeViewItem
类型的字段,也通过接口公开,并且每当创建
TreeView
容器时让
TreeViewItem
自动填充它。

这是通过子类化

TreeView
并将事件附加到
ItemContainerGenerator
来完成的,每当创建
TreeViewItem
时都会记录它。问题包括
TreeViewItem
是懒惰创建的,因此在某些时间可能真的没有可用的。

自从发布这个答案以来,我进一步开发了它并在一个项目中使用了很长时间。到目前为止没有任何问题,除了这违反了 MVVM(但也为您节省了大量简单案例的样板文件)。来源这里

用法

选择所选项目的父项并将其折叠,确保它位于视图中:

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

声明:

<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; }
    ...
}

代码

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; }
}

2
投票

尝试这样的事情:

    public bool UpdateSelectedTreeViewItem(PropertyNode dateItem, ItemsControl itemsControl)
    {
        if (itemsControl == null || itemsControl.Items == null || itemsControl.Items.Count == 0)
        {
            return false;
        }
        foreach (var item in itemsControl.Items.Cast<PropertyNode>())
        {
            var treeViewItem = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (treeViewItem == null)
            {
                continue;
            }
            if (item == dateItem)
            {
                treeViewItem.IsSelected = true;
                return true;
            }
            if (treeViewItem.Items.Count > 0 && UpdateSelectedTreeViewItem(dateItem, treeViewItem))
            {
                return true;
            }
        }
        return false;
    }

2
投票

我将 William 的递归搜索修改为更紧凑的版本:

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;
}

可以通过提供 TreeView 实例的 ItemContainerGenerator 和目标数据对象来调用它:

TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);

2
投票

单个

ItemContainerGenerator.ContainerFromItem
ItemContainerGenerator.ItemContainerGenerator
调用无法找到与树视图对象关联的
TreeViewItem
,因为树视图项控制器和树视图项数据是分离的。 需要创建一个递归函数来使用
ItemContainerGenerator.ContainerFromItem
ItemContainerGenerator.ItemContainerGenerator
,以便在树视图中定位
TreeViewItem

示例递归函数可能如下所示:

private TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object tvio)
{
    var item = container.ContainerFromItem(tvio) as TreeViewItem;
    if (item != null)
    {
        return item;
    }

    for (int i = 0; i < container.Items.Count; i++)
    {
        var subContainer = (TreeViewItem)container.ContainerFromIndex(i);
        if (subContainer != null)
        {
            item = GetTreeViewItemFromObject(subContainer.ItemContainerGenerator, tvio);
            if (item != null)
            {
                return item;
            }
        }
    }

    return null;
}

呼叫者

var target = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, item);

递归函数仅在树视图的

ItemContainerGenerator.Status
ContainersGenerated
后才起作用。所以在初始化视图期间,
GetTreeViewItemFromObject
不起作用。


0
投票

您是否需要 TreeViewItem 因为您要修改正在显示的内容?如果是这种情况,我建议使用 Style 来更改项目的显示方式,而不是使用代码隐藏,而不是直接修改 TreeViewItem。希望它会更干净。


0
投票

如果您必须在孩子的孩子中查找项目,您可能必须使用像这样的递归

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
投票

这是解决方案。 rtvEsa 是树视图。 HierarchicalDataTemplate 是树视图模板 标签使当前项目真正消耗。这不是选定的项目,它是使用 HierarchicalDataTemplate 的树控件中的当前项目。

Items.CurrentItem 是内部树集合的一部分。 您无法获得许多不同的数据。例如 Items.ParenItem 也是如此。

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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


0
投票

我的 TreeView 模板只有一级标题,所以我发现成功了:

        foreach (CollectionViewGroup grp in treeView.Items)
        {
            string strHeader = (string)grp.Name;
            TreeViewItem treeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(grp);
© www.soinside.com 2019 - 2024. All rights reserved.