如何从HierarchicalDataTemplate项中获取TreeViewItem?

时间:2009-03-05 22:27:29

标签: wpf treeview hierarchicaldatatemplate

我有TreeView使用HierarchicalDataTemplate绑定其数据。

看起来像这样:

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

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

this.mainTreeList.SelectedItem;

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

我该怎么做?

11 个答案:

答案 0 :(得分:31)

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;

        ...
     }

答案 1 :(得分:20)

Bea Stollnitz的博客文章中了解相关信息,请尝试

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

答案 2 :(得分:7)

我遇到了同样的问题。 我需要访问TreeViewItem,以便我可以选择它。 然后我意识到我可以在我的ViewModel中添加一个属性IsSelected,然后将其绑定到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。

希望它有所帮助。

答案 3 :(得分:5)

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 :(得分:4)

受Fëanor回答的启发,我试图为TreeViewItem创建的每个数据项轻松访问TreeViewItem

我们的想法是向视图模型添加一个TreeViewItem类型的字段,也通过接口公开,并且只要创建TreeView容器,TreeViewItem就会自动填充它。

这是通过继承TreeView并将事件附加到ItemContainerGenerator来完成的,TreeViewItem会在创建时记录TreeViewItem。陷阱包括... var selected = myTreeView.SelectedItem as MyItem; selected.Parent.TreeViewItem.IsSelected = true; selected.Parent.TreeViewItem.IsExpanded = false; selected.Parent.TreeViewItem.BringIntoView(); ... s是懒惰创建的事实,所以在某些时候可能真的没有。{/ p>

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

<强>用法

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

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

<强>代码

{{1}}

答案 5 :(得分: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;
    }

答案 6 :(得分: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”不起作用。

答案 7 :(得分: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);

答案 8 :(得分:0)

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

答案 9 :(得分: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;
}

答案 10 :(得分:0)

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

Items.CurrentItem是内部树集合的一部分。 你不能得到许多不同的数据。例如Items.ParenItem。

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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