我有三级树视图。如何从代码中选择第三级中的任何项目?我尝试了许多博客和stackoverflow中提到的方法,但它似乎只适用于第一级(对于第一级以下的项目,dbObject为null)。
这是我用来选择TreeViewItem的代码。我错过了什么吗?
public static void SetSelectedItem(this TreeView control, object item)
{
try
{
var dObject = control.ItemContainerGenerator.ContainerFromItem(item);
//uncomment the following line if UI updates are unnecessary
((TreeViewItem)dObject).IsSelected = true;
MethodInfo selectMethod = typeof(TreeViewItem).GetMethod("Select",
BindingFlags.NonPublic | BindingFlags.Instance);
selectMethod.Invoke(dObject, new object[] { true });
}
catch { }
}
答案 0 :(得分:31)
另一种选择是使用绑定。如果您有一个使用绑定的对象来获取每个TreeViewItem
的文本(例如),则可以创建一个也绑定IsSelected
属性的样式:
<TreeView>
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.Resources>
</TreeView>
这假设绑定对象具有IsSelected
类型的bool
属性。然后,您可以通过将TreeViewItem
设置为IsSelected
来为其对应的对象选择true
。
IsExpanded
属性可以使用相同的方法来控制TreeViewItem
展开或折叠的时间。
答案 1 :(得分:7)
您可以使用以下TreeView
扩展程序,我发现这是一个更简单的解决方案:
public static class TreeViewExtension
{
public static bool SetSelectedItem(this TreeView treeView, object item)
{
return SetSelected(treeView, item);
}
private static bool SetSelected(ItemsControl parent, object child)
{
if (parent == null || child == null)
return false;
TreeViewItem childNode = parent.ItemContainerGenerator
.ContainerFromItem(child) as TreeViewItem;
if (childNode != null)
{
childNode.Focus();
return childNode.IsSelected = true;
}
if (parent.Items.Count > 0)
{
foreach (object childItem in parent.Items)
{
ItemsControl childControl = parent
.ItemContainerGenerator
.ContainerFromItem(childItem)
as ItemsControl;
if (SetSelected(childControl, child))
return true;
}
}
return false;
}
}
欲了解更多信息,请阅读此博客文章; http://decompile.it/blog/2008/12/11/selecting-an-item-in-a-treeview-in-wpf/
答案 2 :(得分:4)
尝试不同的解决方案后,我来到this网站。周勇演示了如何以编程方式扩展TreeView的所有节点。他的方法有两个主要观点:
以下是我最终的代码
public static void SelectItem(this ItemsControl parentContainer, List<object> path)
{
var head = path.First();
var tail = path.GetRange(1, path.Count - 1);
var itemContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(head) as TreeViewItem;
if (itemContainer != null && itemContainer.Items.Count == 0)
{
itemContainer.IsSelected = true;
var selectMethod = typeof(TreeViewItem).GetMethod("Select", BindingFlags.NonPublic | BindingFlags.Instance);
selectMethod.Invoke(itemContainer, new object[] { true });
}
else if (itemContainer != null)
{
itemContainer.IsExpanded = true;
if (itemContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
itemContainer.ItemContainerGenerator.StatusChanged += delegate
{
SelectItem(itemContainer, tail);
};
}
else
{
SelectItem(itemContainer, tail);
}
}
}
答案 3 :(得分:1)
在我的情况下(我遇到了同样的问题)但是使用绑定到Data对象的IsSelected属性是不合适的,而且我也不能轻易获得树项的路径,所以下面的代码完美地完成了这项工作: / p>
private void SelectTreeViewItem(object item)
{
try
{
var tvi = GetContainerFromItem(this.MainRegion, item);
tvi.Focus();
tvi.IsSelected = true;
var selectMethod =
typeof(TreeViewItem).GetMethod("Select",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
selectMethod.Invoke(tvi, new object[] { true });
}
catch { }
}
private TreeViewItem GetContainerFromItem(ItemsControl parent, object item)
{
var found = parent.ItemContainerGenerator.ContainerFromItem(item);
if (found == null)
{
for (int i = 0; i < parent.Items.Count; i++)
{
var childContainer = parent.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl;
TreeViewItem childFound = null;
if (childContainer != null && childContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
childContainer.ItemContainerGenerator.StatusChanged += (o, e) =>
{
childFound = GetContainerFromItem(childContainer, item);
};
}
else
{
childFound = GetContainerFromItem(childContainer, item);
}
if (childFound != null)
return childFound;
}
}
return found as TreeViewItem;
}
答案 4 :(得分:0)
是的,即使从直接父TreeViewItem调用它,ContainerFromItem方法也不会返回任何内容。
您可能需要进行一些重新设计。如果您将所有内容创建为显式TreeViewItem,则应该能够保留对它的引用并在其上设置IsSelected。
答案 5 :(得分:0)
对于那些想要一个纯MVVM解决方案的人来说非常晚了,但对于那些想要一个纯MVVM解决方案的人来说,这可以通过事件触发器(用户选择新项目时更新绑定)和数据触发器(更新所选项目)来完成当绑定的值改变时。)
为此,主ViewModel需要项目,当前所选项目的属性以及当前所选项目更改时将调用的命令属性:
public class MainViewModel : ViewModelBase
{
// the currently selected node, can be changed programmatically
private Node _CurrentNode;
public Node CurrentNode
{
get { return this._CurrentNode; }
set { this._CurrentNode = value; RaisePropertyChanged(() => this.CurrentNode); }
}
// called when the user selects a new node in the tree view
public ICommand SelectedNodeChangedCommand { get { return new RelayCommand<Node>(OnSelectedNodeChanged); } }
private void OnSelectedNodeChanged(Node node)
{
this.CurrentNode = node;
}
// list of items to display in the tree view
private ObservableCollection<Node> _Items;
public ObservableCollection<Node> Items
{
get { return this._Items; }
set { this._Items = value; RaisePropertyChanged(() => this.Items); }
}
}
TreeView需要一个事件触发器,用于在选择更改时调用SelectedNodeChangedCommand,以及TreeViewItem样式中的DataTrigger,以便在代码中以编程方式更改CurrentNode的值时选择控件项:
<TreeView x:Name="treeView" ItemsSource="{Binding Items}"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight">
<TreeView.Resources>
<conv:EqualityConverter x:Key="EqualityConverter" />
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="False" />
<Style.Triggers>
<!-- DataTrigger updates TreeViewItem selection when vm code changes CurrentNode -->
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource EqualityConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type TreeView}}" Path="DataContext.CurrentNode" />
<Binding />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="IsSelected" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
<!-- *** HierarchicalDataTemplates go here *** -->
</TreeView.Resources>
<!-- EventTrigger invokes SelectedNodeChangedCommand when selection is changed by user interaction -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<cmd:EventToCommand Command="{Binding SelectedNodeChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
DataTrigger的工作原理是检测CurrentNode的值何时与当前列表项的Node匹配。不幸的是,DataTriggers无法绑定它们的Value,因此它必须使用EqualityConverter进行测试,而不仅仅是进行简单的比较:
public class EqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0] == values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}