如何以编程方式selectWPF TreeView中的项目?
如何以编程方式selectWPF TreeView
的项目? ItemsControl
模型似乎阻止它。
这真是一个很奇怪的原因,你必须使用ContainerFromItem来获取容器,然后调用select方法。 有关于如何在这里做的博客条目。
对于那些仍然在寻找正确的解决scheme,这里是下面的问题。 我在DaWanderer的代码项目文章“WPF TreeViewselect” http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx的评论中发现了这个。; 它由Kenrae于2008年11月25日发布。这对我很好。 谢谢Kenrae!
这是他的post:
让自己的数据对象具有IsSelected属性(并且我也推荐IsExpanded属性),而不是走树。 使用TreeView上的ItemContainerStyle属性定义树的TreeViewItems的样式,TreeView将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}/>
您需要获取TreeViewItem
,然后将IsSelected
设置为true
。
这并不像看起来那么简单,Steven提供的链接在2008年发布了一个解决scheme,该解决scheme仍然可以工作,但是并不关心虚拟化TreeView。 而且在那篇文章的评论中还提到了许多其他问题。 没有罪行,但我也陷入了同样的问题,无法find一个完美的解决scheme。 这里是一些文章/post的链接,这些帮助我很多 –
如何扩展TreeView中的项目? – 第三部分: http : //bea.stollnitz.com/blog/?p=59
以编程方式在TreeView中select一个项目: http : //blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond
TreeView,TreeViewItem和IsSelected: http ://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/
我已经成功的这个代码:
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; }
用法:
var tvi = FindTviFromObjectRecursive(TheTreeView, TheModel); if (tvi != null) tvi.IsSelected = true;
我写了一个扩展方法:
using System.Windows.Controls; namespace Extensions { public static class TreeViewEx { /// <summary> /// Select specified item in a TreeView /// </summary> public static void SelectItem(this TreeView treeView, object item) { var tvItem = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem; if (tvItem != null) { tvItem.IsSelected = true; } } } }
我可以这样使用:
if (_items.Count > 0) _treeView.SelectItem(_items[0]);
试试这个
/// <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; }
参考: http : //amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html
只是以为我会与我一起解决的解决scheme,以防万一这可以帮助任何人。 请注意,最好的方法是按照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; } } }
这样做是select并展开代表后面代码中所选数据项的UI中的treeview项目。 这样做的目的是在用户select在同一窗口中的项目控件中更改时,在树视图中进行select更改。
如果你想select位于孩子的孩子,你可以用recursion来做到这一点。
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; }
我已经创build了一个方法VisualTreeExt.GetDescendants<T>
,它返回一个匹配指定types的可枚举的元素集合:
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; } } }
当你问VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView)
你会得到所有的TreeViewItem
孩子。 您可以使用下面的一段代码select一个特定的值:
var treeViewItem = VisualTreeExt.GetDescendants<TreeViewItem>(MyTreeView).FirstOrDefault(tvi => tvi.DataContext == newValue); if (treeViewItem != null) treeViewItem.IsSelected = true;
这是一个肮脏的解决scheme(可能不是最有效的),如果你使用虚拟化的TreeView,将不会工作,因为它取决于实际的视觉元素的存在。 但它适用于我的情况…
是的..我知道很多年以来的问题,但问题仍然没有快速解决这个问题..所以:
以下将做OP所要求的。
我基本上做的是阅读本页面的所有答案,并遵循所有相关的链接,以创build一个一劳永逸的解决这个令人烦恼的问题。
优点:
- 它也支持虚拟化TreeView。
- 它使用行为技术,所以XAML是很容易的。
- 添加依赖属性以允许绑定到所选的TreeView项目。
这部分是你需要复制的唯一代码,其他部分只是帮助完成一个例子。
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 }
这是TreeView在XAML中的外观样例:
<TreeView x:Name="trvwSs" Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}" behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />
唯一需要担心的是确保你将要绑定到SelectedItemEx的view-model属性不为null。 但是这不是一个特例。刚才提到它会让人感到困惑。
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))); } } }
最后一件事情..以编程方式select的例子:我在我的MainWindow.xaml和处理程序上创build了一个button..
private void Button_Click(object sender, RoutedEventArgs e) { TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]); //TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null); }
希望这可以帮助别人:)
你可以通过后面的代码来完成
if (TreeView1.Items.Count > 0) (TreeView1.Items[0] as TreeViewItem).IsSelected = true;