尽管接受的答案在大多数情况下都会起作用。它可能碰巧不起作用,因为该对象是在另一个完全不受调度程序控制的线程上创建的。
如前所述,问题在于 TreeViewItem 是在调度程序的另一个线程中创建的。
我个人认为正确的解决方案更复杂。我知道这很糟糕,但我真的认为是这样。无论是否虚拟化,我的代码都应该可以工作。此外,您可以删除任何不需要的参考(我没有验证)。
我的解决方案基于一个数据模型,其中每个节点都继承自同一个根:MultiSimBase 对象,但这不是必需的。
一切都从激活(+设置焦点并显示)新添加的项目的 SetSelectedTreeViewItem() 开始。
希望它可以帮助或启发一些...快乐编码!
表格代码:
// ******************************************************************
private List<MultiSimBase> SetPathListFromRootToNode(MultiSimBase multiSimBase, List<MultiSimBase> listTopToNode = null)
{
if (listTopToNode == null)
{
listTopToNode = new List<MultiSimBase>();
}
listTopToNode.Insert(0, multiSimBase);
if (multiSimBase.Parent != null)
{
SetPathListFromRootToNode(multiSimBase.Parent, listTopToNode);
}
return listTopToNode;
}
// ******************************************************************
private void SetSelectedTreeViewItem(MultiSimBase multiSimBase)
{
List<MultiSimBase> listOfMultiSimBasePathFromRootToNode = SetPathListFromRootToNode(multiSimBase);
TreeViewStudy.SetItemHierarchyVisible(listOfMultiSimBasePathFromRootToNode, (tvi) =>
{
tvi.IsSelected = true;
tvi.Focus();
tvi.BringIntoView();
});
}
现在通用代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
namespace HQ.Util.Wpf.WpfUtil
{
public static class TreeViewExtensions
{
public delegate void OnTreeViewVisible(TreeViewItem tvi);
private static void SetItemHierarchyVisible(ItemContainerGenerator icg, IList listOfRootToNodePath, OnTreeViewVisible onTreeViewVisible = null)
{
Debug.Assert(icg != null);
if (icg != null)
{
if (listOfRootToNodePath.Count == 0) // nothing to do
return;
TreeViewItem tvi = icg.ContainerFromItem(listOfRootToNodePath[0]) as TreeViewItem;
if (tvi != null) // Due to threading, always better to verify
{
listOfRootToNodePath.RemoveAt(0);
if (listOfRootToNodePath.Count == 0)
{
if (onTreeViewVisible != null)
onTreeViewVisible(tvi);
}
else
{
if (!tvi.IsExpanded)
tvi.IsExpanded = true;
SetItemHierarchyVisible(tvi.ItemContainerGenerator, listOfRootToNodePath, onTreeViewVisible);
}
}
else
{
ActionHolder actionHolder = new ActionHolder();
EventHandler itemCreated = delegate(object sender, EventArgs eventArgs)
{
var icgSender = sender as ItemContainerGenerator;
tvi = icgSender.ContainerFromItem(listOfRootToNodePath[0]) as TreeViewItem;
if (tvi != null) // Due to threading, it is always better to verify
{
SetItemHierarchyVisible(icg, listOfRootToNodePath, onTreeViewVisible);
actionHolder.Execute();
}
};
actionHolder.Action = new Action(() => icg.StatusChanged -= itemCreated);
icg.StatusChanged += itemCreated;
return;
}
}
}
// ******************************************************************
/// <summary>
/// You cannot rely on this method to be synchronous. If you have any action that depend on the TreeViewItem
/// (last item of collectionOfRootToNodePath) to be visible, you should set it in the 'onTreeViewItemVisible' method.
/// This method should work for Virtualized and non virtualized tree.
/// </summary>
/// <param name="treeView">TreeView where an item has to be set visible</param>
/// <param name="collectionOfRootToNodePath">Any of collection that implement ICollection like a generic List.
/// The collection should have every objet of the path to the targeted item from the top to the target.
/// For example for an apple tree: AppleTree (index 0), Branch4, SubBranch3, Leaf2 (index 3)</param>
/// <param name="onTreeViewVisible">Optionnal</param>
public static void SetItemHierarchyVisible(this TreeView treeView, IList listOfRootToNodePath, OnTreeViewVisible onTreeViewVisible = null)
{
ItemContainerGenerator icg = treeView.ItemContainerGenerator;
if (icg == null)
return; // Is tree loaded and initialized ???
SetItemHierarchyVisible(icg, listOfRootToNodePath, onTreeViewVisible);
}
和
using System;
namespace HQ.Util.Wpf.WpfUtil
{
// Requested to unsubscribe into an anonymous method that is a delegate used for a one time execution
// http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/df2773eb-0cc1-4f3a-a674-e32f2ef2c3f1/
public class ActionHolder
{
public void Execute()
{
if (Action != null)
{
Action();
}
}
public Action Action { get; set; }
}
}