C# 如何以编程方式选择 WPF TreeView 中的项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/413890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to programmatically select an item in a WPF TreeView?
提问by Thomas Bratt
How is it possible to programmatically select an item in a WPF TreeView
? The ItemsControl
model seems to prevent it.
如何以编程方式选择 WPF 中的项目TreeView
?该ItemsControl
模型似乎阻止了它。
采纳答案by Steven Robbins
It's a real pain for some strange reason, you have to use ContainerFromItem to get the container, then invoke the select method.
由于某些奇怪的原因,这真的很痛苦,您必须使用 ContainerFromItem 来获取容器,然后调用 select 方法。
// selectedItemObject is not a TreeViewItem, but an item from the collection that
// populated the TreeView.
var tvi = treeView.ItemContainerGenerator.ContainerFromItem(selectedItemObject)
as TreeViewItem;
if (tvi != null)
{
tvi.IsSelected = true;
}
There once was a blog entry on how to do it here, but the link is dead now.
回答by Kent Boogaart
You need to get the TreeViewItem
and then set IsSelected
to true
.
您需要获取TreeViewItem
,然后设置IsSelected
为true
。
回答by akjoshi
This is not as simple as it looks, the link provided by Steven has a solution posted in 2008, which may still works but doesn't take care of Virtualized TreeViews. Moreover many other problems are mentioned in comments of that article. No offences, but I am also stuck with same problem and can't find a perfect solution. Here are the links to some of the articles/posts which helped me a lot-
这并不像看起来那么简单,Steven 提供的链接在 2008 年发布了一个解决方案,该解决方案可能仍然有效,但不会处理 Virtualized TreeViews。此外,该文章的评论中还提到了许多其他问题。没有冒犯,但我也遇到了同样的问题,找不到完美的解决方案。以下是一些对我有很大帮助的文章/帖子的链接-
How can I expand items in a TreeView? – Part III: http://bea.stollnitz.com/blog/?p=59
如何在 TreeView 中展开项目?– 第三部分:http: //bea.stollnitz.com/blog/?p=59
Programmatically Selecting an Item in a TreeView: http://blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond
以编程方式在 TreeView 中选择一个项目:http: //blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond
TreeView, TreeViewItem and IsSelected: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/
TreeView、TreeViewItem 和 IsSelected:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/
回答by kuninl
For those who are still looking for the right solution to this problem here is the one below. I found this one in the comments to the Code Project article “WPF TreeView Selection” http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspxby DaWanderer. It was posted by Kenrae on Nov 25 2008. This worked great for me. Thanks Kenrae!
对于那些仍在寻找此问题的正确解决方案的人,这里是下面的一个。我在DaWanderer对代码项目文章“WPF TreeView 选择” http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx的评论中发现了这一点。它由 Kenrae 于 2008 年 11 月 25 日发布。这对我很有用。谢谢肯莱!
Here is his post:
这是他的帖子:
Instead of walking the tree, have your own data object have the IsSelected property (and I recommend the IsExpanded property too). Define a style for the TreeViewItems of the tree using the ItemContainerStyle property on the TreeView that binds those properties from the TreeViewItem to your data objects. Something like this:
不要遍历树,而是让您自己的数据对象具有 IsSelected 属性(我也推荐 IsExpanded 属性)。使用 TreeView 上的 ItemContainerStyle 属性为树的 TreeViewItems 定义样式,该属性将 TreeViewItem 中的这些属性绑定到您的数据对象。像这样的东西:
<Style x:Key="LibraryTreeViewItemStyle"
TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight"
Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="FontWeight"
Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
<TreeView ItemsSource="{Binding Path=YourCollection}"
ItemContainerStyle="{StaticResource LibraryTreeViewItemStyle}"
ItemTemplate={StaticResource YourHierarchicalDataTemplate}/>
回答by AmirMehdi KhademAstaneh
Try with this
试试这个
/// <summary>
/// Selects the tree view item.
/// </summary>
/// <param name="Collection">The collection.</param>
/// <param name="Value">The value.</param>
/// <returns></returns>
private TreeViewItem SelectTreeViewItem(ItemCollection Collection, String Value)
{
if (Collection == null) return null;
foreach(TreeViewItem Item in Collection)
{
/// Find in current
if (Item.Header.Equals(Value))
{
Item.IsSelected = true;
return Item;
}
/// Find in Childs
if (Item.Items != null)
{
TreeViewItem childItem = this.SelectTreeViewItem(Item.Items, Value);
if (childItem != null)
{
Item.IsExpanded = true;
return childItem;
}
}
}
return null;
}
Reference: http://amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html
参考:http: //amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html
回答by RobJohnson
Just thought I would chime in with the solution I went with, in case this can help anyone. Note that the best way to do this is using a bound property like 'IsSelected' as per kuninl's answer, but in my case it was a legacy application that did not follow MVVM, so I ended up with the below.
只是想我会加入我采用的解决方案,以防这可以帮助任何人。请注意,执行此操作的最佳方法是根据 kuninl 的回答使用“IsSelected”之类的绑定属性,但在我的情况下,它是一个未遵循 MVVM 的遗留应用程序,因此我最终得到了以下内容。
private void ChangeSessionSelection()
{
foreach (SessionContainer item in this.treeActiveSessions.Items)
{
var treeviewItem = this.treeActiveSessions.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (item.Session == this.selectedSession.Session)
{
treeviewItem.IsSelected = true;
treeviewItem.IsExpanded = true;
}
else
{
treeviewItem.IsSelected = false;
treeviewItem.IsExpanded = false;
}
}
}
What this does is select and expand the treeview item in the UI that represents the selected dataitem in the code behind. The purpose of this was to have the selection change in the treeview when the users selection changed in an items control in the same window.
这样做是在 UI 中选择并展开表示所选数据项的树视图项。这样做的目的是当用户选择在同一窗口中的项目控件中更改时,树视图中的选择更改。
回答by purvin
If you want to select item located withing children of child you can user recursion to do that.
如果您想选择位于孩子的孩子的项目,您可以使用递归来做到这一点。
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;
}
回答by Ramon de Klein
I have created a method VisualTreeExt.GetDescendants<T>
that returns an enumerable collection of elements that match the specified type:
我创建了一个方法VisualTreeExt.GetDescendants<T>
,该方法返回与指定类型匹配的元素的可枚举集合:
public static class VisualTreeExt
{
public static IEnumerable<T> GetDescendants<T>(DependencyObject parent) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; ++i)
{
// Obtain the child
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
yield return (T)child;
// Return all the descendant children
foreach (var subItem in GetDescendants<T>(child))
yield return subItem;
}
}
}
When you ask for VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView)
you'll get all the TreeViewItem
childs. You can select a particular value using the following piece of code:
当你要求时,VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView)
你会得到所有的TreeViewItem
孩子。您可以使用以下代码选择特定值:
var treeViewItem = VisualTreeExt.GetDescendants<TreeViewItem>(MyTreeView).FirstOrDefault(tvi => tvi.DataContext == newValue);
if (treeViewItem != null)
treeViewItem.IsSelected = true;
It's a bit of a dirty solution (and probably not the most efficient) and won't work if you're using a virtualized TreeView, because it depends on the existance of the actual visual elements. But it works for my situation...
这是一个有点肮脏的解决方案(并且可能不是最有效的)并且如果您使用虚拟化的 TreeView 将不起作用,因为它取决于实际视觉元素的存在。但它适用于我的情况......
回答by Fandi Susanto
I've succeeded with this code:
我已成功使用此代码:
public static TreeViewItem FindTviFromObjectRecursive(ItemsControl ic, object o) {
//Search for the object model in first level children (recursively)
TreeViewItem tvi = ic.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem;
if (tvi != null) return tvi;
//Loop through user object models
foreach (object i in ic.Items) {
//Get the TreeViewItem associated with the iterated object model
TreeViewItem tvi2 = ic.ItemContainerGenerator.ContainerFromItem(i) as TreeViewItem;
tvi = FindTviFromObjectRecursive(tvi2, o);
if (tvi != null) return tvi;
}
return null;
}
Usage:
用法:
var tvi = FindTviFromObjectRecursive(TheTreeView, TheModel);
if (tvi != null) tvi.IsSelected = true;
回答by G.Y
Yeah.. I know many years past since the question was asked but.. still no quick solution to this problem.. and So:
是的..我知道这个问题已经过去很多年了但是..仍然没有快速解决这个问题..所以:
The following will do what the OP asked for.
以下将执行 OP 要求的操作。
What I basically done is reading all the answers in this page and following all the relevant links to create a once and for allsolution to this irritating problem.
我基本上所做的是阅读此页面中的所有答案,并按照所有相关链接为这个恼人的问题创建一个一劳永逸的解决方案。
Benefits:
好处:
- It support Virtualizing TreeView as well.
- It using the behavior technique, so XAML is way easy.
- Adds a dependancy-property to allow binding to the selected TreeView Item.
- 它也支持虚拟化 TreeView。
- 它使用行为技术,因此 XAML 很容易。
- 添加一个依赖属性以允许绑定到选定的 TreeView 项。
This part is the only code you need to copy, the other parts are just to help complete an example.
这部分是你唯一需要复制的代码,其他部分只是帮助完成一个例子。
public static class TreeViewSelectedItemExBehavior
{
private static List<TreeView> isRegisteredToSelectionChanged = new List<TreeView>();
public static readonly DependencyProperty SelectedItemExProperty =
DependencyProperty.RegisterAttached("SelectedItemEx",
typeof(object),
typeof(TreeViewSelectedItemExBehavior),
new FrameworkPropertyMetadata(new object(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemExChanged, null));
#region SelectedItemEx
public static object GetSelectedItemEx(TreeView target)
{
return target.GetValue(SelectedItemExProperty);
}
public static void SetSelectedItemEx(TreeView target, object value)
{
target.SetValue(SelectedItemExProperty, value);
var treeViewItemToSelect = GetTreeViewItem(target, value);
if (treeViewItemToSelect == null)
{
if (target.SelectedItem == null)
return;
var treeViewItemToUnSelect = GetTreeViewItem(target, target.SelectedItem);
treeViewItemToUnSelect.IsSelected = false;
}
else
treeViewItemToSelect.IsSelected = true;
}
public static void OnSelectedItemExChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var treeView = depObj as TreeView;
if (treeView == null)
return;
if (!isRegisteredToSelectionChanged.Contains(treeView))
{
treeView.SelectedItemChanged += TreeView_SelectedItemChanged;
isRegisteredToSelectionChanged.Add(treeView);
}
}
#endregion
private static void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var treeView = (TreeView)sender;
SetSelectedItemEx(treeView, e.NewValue);
}
#region Helper Structures & Methods
public class MyVirtualizingStackPanel : VirtualizingStackPanel
{
/// <summary>
/// Publically expose BringIndexIntoView.
/// </summary>
public void BringIntoView(int index)
{
BringIndexIntoView(index);
}
}
/// <summary>Recursively search for an item in this subtree.</summary>
/// <param name="container">The parent ItemsControl. This can be a TreeView or a TreeViewItem.</param>
/// <param name="item">The item to search for.</param>
/// <returns>The TreeViewItem that contains the specified item.</returns>
private static TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
if (container != null)
{
if (container.DataContext == item)
{
return container as TreeViewItem;
}
// Expand the current container
if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded)
{
container.SetValue(TreeViewItem.IsExpandedProperty, true);
}
// Try to generate the ItemsPresenter and the ItemsPanel.
// by calling ApplyTemplate. Note that in the
// virtualizing case even if the item is marked
// expanded we still need to do this step in order to
// regenerate the visuals because they may have been virtualized away.
container.ApplyTemplate();
ItemsPresenter itemsPresenter =
(ItemsPresenter)container.Template.FindName("ItemsHost", container);
if (itemsPresenter != null)
{
itemsPresenter.ApplyTemplate();
}
else
{
// The Tree template has not named the ItemsPresenter,
// so walk the descendents and find the child.
itemsPresenter = FindVisualChild<ItemsPresenter>(container);
if (itemsPresenter == null)
{
container.UpdateLayout();
itemsPresenter = FindVisualChild<ItemsPresenter>(container);
}
}
Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0);
// Ensure that the generator for this panel has been created.
UIElementCollection children = itemsHostPanel.Children;
MyVirtualizingStackPanel virtualizingPanel =
itemsHostPanel as MyVirtualizingStackPanel;
for (int i = 0, count = container.Items.Count; i < count; i++)
{
TreeViewItem subContainer;
if (virtualizingPanel != null)
{
// Bring the item into view so
// that the container will be generated.
virtualizingPanel.BringIntoView(i);
subContainer =
(TreeViewItem)container.ItemContainerGenerator.
ContainerFromIndex(i);
}
else
{
subContainer =
(TreeViewItem)container.ItemContainerGenerator.
ContainerFromIndex(i);
// Bring the item into view to maintain the
// same behavior as with a virtualizing panel.
subContainer.BringIntoView();
}
if (subContainer != null)
{
// Search the next level for the object.
TreeViewItem resultContainer = GetTreeViewItem(subContainer, item);
if (resultContainer != null)
{
return resultContainer;
}
else
{
// The object is not under this TreeViewItem
// so collapse it.
subContainer.IsExpanded = false;
}
}
}
}
return null;
}
/// <summary>Search for an element of a certain type in the visual tree.</summary>
/// <typeparam name="T">The type of element to find.</typeparam>
/// <param name="visual">The parent element.</param>
/// <returns></returns>
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
}
#endregion
}
And this is an example of how the TreeView line looks like in XAML:
这是 TreeView 行在 XAML 中的外观示例:
<TreeView x:Name="trvwSs"
Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}"
behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />
Only thing to worry about is to make sure your view-model property that you about to bound to SelectedItemEx is not null. But that is not a special case.. Just mentioned it in case people get confused.
唯一需要担心的是确保您将要绑定到 SelectedItemEx 的视图模型属性不为空。但这不是一个特例.. 只是提到它以防人们感到困惑。
public class VmMainContainer : INotifyPropertyChanged
{
private object selectedItemTreeViewSs = new object();
private ObservableCollection<object> selectedItemsTreeViewSs = new ObservableCollection<object>();
private ObservableCollection<VmItem> itemsTreeViewSs = new ObservableCollection<VmItem>();
public object SelectedItemTreeViewSs
{
get
{
return selectedItemTreeViewSs;
}
set
{
selectedItemTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemTreeViewSs)));
}
}
public ObservableCollection<object> SelectedItemsTreeViewSs
{
get
{
return selectedItemsTreeViewSs;
}
set
{
selectedItemsTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsTreeViewSs)));
}
}
public ObservableCollection<VmItem> ItemsTreeViewSs
{
get { return itemsTreeViewSs; }
set
{
itemsTreeViewSs = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemsTreeViewSs)));
}
}
}
And last thing.. example of selecting programmatically: I created a button on my MainWindow.xaml and from its handler..
最后一件事.. 以编程方式选择的示例:我在 MainWindow.xaml 及其处理程序中创建了一个按钮..
private void Button_Click(object sender, RoutedEventArgs e)
{
TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]);
//TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null);
}
Hope this helps someone :)
希望这对某人有所帮助:)