我有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
对象(甚至不知道如何)。
我该怎么做?
答案 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 }" />