如何通过编程方式在 WPF
TreeView
中选择项目?ItemsControl
模型似乎会阻止此操作。TreeView
中选择项目?ItemsControl
模型似乎会阻止此操作。试试这个
/// <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
是的..我知道自从问题被提出已经过去了很多年,但是..仍然没有快速解决这个问题的方法..所以:
以下将会做到OP所要求的。
我基本上所做的就是阅读了此页面中所有答案并跟随所有相关链接来创建一个一劳永逸的解决方案来解决这个恼人的问题。
好处:
这部分是您需要复制的唯一代码,其他部分只是帮助完成示例。
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
}
这是一个在XAML中TreeView外观的例子:
<TreeView x:Name="trvwSs"
Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}"
behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />
唯一需要担心的是确保您要绑定到SelectedItemEx的视图模型属性不为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)));
}
}
}
最后一件事...编程选择的示例: 我在MainWindow.xaml上创建了一个按钮,并从其处理程序中...
private void Button_Click(object sender, RoutedEventArgs e)
{
TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]);
//TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null);
}
TreeView
。此外,您从订阅的事件中永远不会注销。如果元素被删除,您的行为仍将保留对它们的引用。有一些不错的工作可以拿出来,但整体上是有漏洞的。 - Shimmy Weitzhandlerprivate 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;
}
}
}
public class TreeViewHelper<TModel>
{
public TreeViewHelper(TreeView treeView, Func<TModel, TModel> getParent, Func<TModel, IList<TModel>> getSubItems)
{
TreeView = treeView;
GetParent = getParent;
GetSubItems = getSubItems;
}
public TreeView TreeView { get; }
public Func<TModel, TModel> GetParent { get; }
public Func<TModel, IList<TModel>> GetSubItems { get; }
public void SelectItemWhileLoaded(TModel node, IList<TModel> rootNodes)
{
if (TreeView.IsLoaded)
{
SelectItem(node, rootNodes);
}
else
{
TreeView.Loaded += TreeView_Loaded;
void TreeView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
TreeView.Loaded -= TreeView_Loaded;
SelectItem(node, rootNodes);
}
}
}
public void SelectItem(TModel node, IList<TModel> rootNodes)
{
Stack<TModel> nodes = new Stack<TModel>();
//push into stack
while (!rootNodes.Contains(node))
{
nodes.Push(node);
node = GetParent(node);
}
TreeViewItem treeViewItem = TreeView.ItemContainerGenerator
.ContainerFromItem(node) as TreeViewItem;
if (nodes.Count == 0)
{
//Top level
treeViewItem.IsSelected = true;
treeViewItem.BringIntoView();
return;
}
Expanded(true);
void Expanded(bool top)
{
if (!top)
{
treeViewItem = treeViewItem.ItemContainerGenerator
.ContainerFromItem(node) as TreeViewItem;
if (nodes.Count == 0)
{
treeViewItem.IsSelected = true;
treeViewItem.BringIntoView();
return;
}
}
node = nodes.Pop();
treeViewItem.IsExpanded = true;
if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
Expanded(true);
}
else
{
//Lazy
treeViewItem.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
treeViewItem.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
Expanded(false);
}
}
}
}
TreeViewItem FindTreeViewItem( Stack<object> nodeStack, TreeView treeView )
{
ItemsControl itemsControl = treeView;
while (nodeStack.Count > 0) {
object node = nodeStack.Pop();
bool found = false;
foreach (object item in itemsControl.Items) {
if (item == node) {
found = true;
if (itemsControl.ItemContainerGenerator.ContainerFromItem( item ) is TreeViewItem treeViewItem) {
if (nodeStack.Count == 0) {
return treeViewItem;
}
itemsControl = treeViewItem;
treeViewItem.IsExpanded = true;
treeViewItem.UpdateLayout();
break;
}
}
}
if (!found) {
return null;
}
}
return null;
}
调用示例:
// Build nodeStack here from your data
TreeViewItem treeViewItem = FindTreeViewItem( nodeStack, treeView );
if (treeViewItem != null) {
treeViewItem.IsSelected = true;
treeViewItem.BringIntoView();
}
我认为这是最简单的解决方案:
private void MouseDownEventProcessing(TreeNodeMouseClickEventArgs e)
{
tvEmployeeDirectory.SelectedNode = e.Node;
}