Wpf 如何从代码中选择TreeView项
我有一个三层的树景。如何从代码中选择第三级中的任何项目?我尝试了许多博客和stackoverflow中提到的一种方法,但它似乎只适用于第一级(对于低于第一级的项目,dbObject为null) 下面是我用来选择TreeViewItem的代码。我错过什么了吗Wpf 如何从代码中选择TreeView项,wpf,treeview,Wpf,Treeview,我有一个三层的树景。如何从代码中选择第三级中的任何项目?我尝试了许多博客和stackoverflow中提到的一种方法,但它似乎只适用于第一级(对于低于第一级的项目,dbObject为null) 下面是我用来选择TreeViewItem的代码。我错过什么了吗 public static void SetSelectedItem(this TreeView control, object item) { try { var dObject = control.Item
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 { }
}
是的,ContainerFromItem方法不会返回任何东西,即使您从直接父树项调用它
你可能需要重新设计一下。如果您将所有内容都创建为显式的TreeView项目,您应该能够保留对它的引用并在其上设置IsSelected。在尝试了不同的解决方案后,我来到了该站点。周勇展示了如何通过编程扩展TreeView的所有节点。他的方法有两个主要观点:
- ContainerFromItem仅当项是元素的直接子项时才返回容器。在TreeView中,这意味着只返回第一级子容器,您必须在子TreeView项上调用ContainerFromItem才能从下一级获取容器
- 要使ContainerFromItem工作,应创建TreeViewItem的可视子项,并且仅当TreeViewItem展开时才会发生这种情况。这意味着要选择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);
}
}
}
public static void SelectItem(此项控制父容器,列表路径)
{
var head=path.First();
var tail=path.GetRange(1,path.Count-1);
var itemContainer=parentContainer.ItemContainerGenerator.ContainerFromItem(head)作为TreeViewItem;
if(itemContainer!=null&&itemContainer.Items.Count==0)
{
itemContainer.IsSelected=true;
var selectMethod=typeof(treevieItem).GetMethod(“选择”,BindingFlags.NonPublic | BindingFlags.Instance);
调用(itemContainer,新对象[]{true});
}
else if(itemContainer!=null)
{
itemContainer.IsExpanded=true;
if(itemContainer.ItemContainerGenerator.Status!=GeneratorStatus.ContainerGenerated)
{
itemContainer.ItemContainerGenerator.StatusChanged+=委托
{
选择Item(itemContainer,tail);
};
}
其他的
{
选择Item(itemContainer,tail);
}
}
}
另一种选择是使用绑定。如果您有一个对象正在使用绑定来获取每个treevieItem
(例如)的文本,您可以创建一个样式来绑定IsSelected
属性:
<TreeView>
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.Resources>
</TreeView>
这假设绑定对象具有类型为bool
的IsSelected
属性。然后,您可以通过将对应对象的IsSelected
设置为true
来选择TreeViewItem
同样的方法也可以用于IsExpanded
属性来控制TreeViewItem
何时展开或折叠。在我的情况下(我也有同样的问题),但是使用绑定到数据对象的IsSelected属性是不合适的,而且我也无法轻松获得到树项的路径,因此,以下代码完美地完成了这项工作:
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;
}
private void SelectTreeViewItem(对象项)
{
尝试
{
var tvi=GetContainerFromItem(this.MainRegion,item);
tvi.Focus();
tvi.IsSelected=true;
变量选择法=
typeof(TreeViewItem).GetMethod(“选择”,
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
调用(tvi,新对象[]{true});
}
捕获{}
}
私有TreeViewItem GetContainerFromItem(ItemsControl父项,对象项)
{
var found=parent.ItemContainerGenerator.ContainerFromItem(item);
if(found==null)
{
对于(int i=0;i
{
childFound=GetContainerFromItem(childContainer,item);
};
}
其他的
{
childFound=GetContainerFromItem(childContainer,item);
}
if(childFound!=null)
返回找到的孩子;
}
}
返回被发现为TreeViewItem;
}
您可以使用以下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;
}
}
欲了解更多信息,请阅读此博客文章;
我的答案很晚才到达,但对于那些想要纯MVVM解决方案的人来说,这可以通过事件触发器(当用户选择新项目时更新绑定)和数据触发器(当绑定值更改时更新所选项目)来完成 为此,主视图模型需要项目、当前选定项目的属性以及当前选定项目更改时将调用的命令属性:
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); }
}
}
是的,我知道这一点。但它似乎引入了代码耦合。不管怎样,你在这里有这个答案是很好的。浏览此页面的人可能更喜欢您的方式mine@Andy当前位置在Silverlight中如何执行此操作?我尝试此代码获取错误
无法设置只读属性“”。
@Navid我不确定是否可以。我
<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>
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();
}
}