36

我有一个TreeView使用 aHierarchicalDataTemplate来绑定它的数据。

它看起来像这样:

<TreeView x:Name="mainTreeList" ItemsSource="{Binding MyCollection}>
  <TreeView.Resources>
    <HierarchicalDataTemplate 
     DataType="{x:Type local:MyTreeViewItemViewModel}" 
     ItemsSource="{Binding Children}">
      <!-- code code code -->
    </HierarchicalDataTemplate>
  </TreeView.Resources>
</TreeView>

现在,从主窗口的代码隐藏中,我想获取当前 selected TreeViewItem。但是,如果我使用:

this.mainTreeList.SelectedItem;

selectedItem 的类型为MyTreeViewItemViewModel。但我想得到 'parent' 或 'bound' TreeViewItem。我没有将它传递给我的TreeViewItemModel对象(甚至不知道如何)。

我怎样才能做到这一点?

4

11 回答 11

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

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

使用 HierarchicalDataTemplate 的树视图中的mainTreeList.Items.CurrentItem以下都不会始终为空。

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

相反,我必须在路由的 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;

        ...
     }
于 2010-07-05T04:59:02.210 回答
24

Bea Stollnitz的博客文章中,尝试

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));
于 2009-03-06T03:03:08.020 回答
9

我遇到了同样的问题。我需要到达 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 。

希望它可以帮助某人。

于 2011-09-21T17:20:45.803 回答
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)));

这对我来说效果更好。

于 2013-01-09T10:39:21.077 回答
4

受 Fëanor 的回答的启发,我试图让创建TreeViewItema 的每个数据项都易于访问TreeViewItem

这个想法是向TreeViewItem视图模型添加一个 -typed 字段,也通过接口公开,并在创建容器TreeView时自动填充它。TreeViewItem

这是通过子类TreeView化并将一个事件附加到 来完成的ItemContainerGenerator,该事件记录了TreeViewItem它的创建时间。陷阱包括TreeViewItems 是懒惰地创建的,因此在某些时候可能真的没有可用的。

自从发布这个答案以来,我已经进一步开发并在一个项目中使用了很长时间。到目前为止没有任何问题,除了这违反了 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; }
}
于 2012-03-26T13:33:13.073 回答
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;
    }
于 2015-02-07T05:04:01.247 回答
1

由于树视图项目控制器和树视图项目数据的分离,单个“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”不起作用。

于 2018-06-08T19:36:17.717 回答
1

我将 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);
于 2019-01-11T19:46:07.273 回答
0

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

于 2009-09-22T02:47:08.580 回答
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;
}
于 2013-09-06T11:02:52.780 回答
0

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

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

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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

于 2014-10-09T07:28:38.410 回答