I have a custom tree view user control that is using a Virtualizing Stack Panel since i have to display ~8000 items. The tree view also lives in a groupbox with custom SearchBox user control that allows users to search a tree view item and select it from the searchbox results.
The Search Box has a dependency property for the SelectedItem and the custom tree view also has a SelectedItem property that get set on the SelectionChanged event. in my view model i am binding a single SelectedItem property to both dependency properties stated above.
Since i am using a virtualizing stack panel and selecting items from the search box, I needed some additional logic to find the TreeViewItem that matched the selectedItem in the search box, for this I followed the following article: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/how-to-find-a-treeviewitem-in-a-treeview?view=netframeworkdesktop-4.8. I made my own implementation so that it was faster.
Here is the code:
private static TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
if (container == null)
return null;
var rootFolder = (Folder)container.DataContext;
var folderTreeItem = (IFolderTreeItem)item;
var parentStack = new Stack<Folder>();
var parentFolder = folderTreeItem.ParentFolder;
while (parentFolder != rootFolder)
{
parentStack.Push(parentFolder);
parentFolder = parentFolder.ParentFolder;
}
var rootTreeViewItem = (TreeViewItem)container;
rootTreeViewItem.BringIntoView();
rootTreeViewItem.IsExpanded = true;
var virtualizingPanel = GetVirtualizingStackPanel(container);
var currentFolder = rootFolder;
while (parentStack.Count > 0)
{
var childFolder = parentStack.Pop();
var childIndex = currentFolder.SubFolders.IndexOf(childFolder);
if (childIndex == -1)
throw new Exception();
virtualizingPanel.BringIntoView(childIndex);
var currentTreeViewItem = (TreeViewItem)rootTreeViewItem.ItemContainerGenerator.ContainerFromIndex(childIndex);
currentTreeViewItem.IsExpanded = true;
currentFolder = childFolder;
rootTreeViewItem = currentTreeViewItem;
virtualizingPanel = GetVirtualizingStackPanel(currentTreeViewItem);
}
var currentIndex = rootTreeViewItem.ItemContainerGenerator.Items.Cast<IFolderTreeItem>().ToList().FindIndex(tv => tv.Equals( folderTreeItem));
virtualizingPanel.BringIntoView(currentIndex);
var treeView = (TreeViewItem)rootTreeViewItem.ItemContainerGenerator.ContainerFromIndex(currentIndex);
return treeView;
}
Here is what my data model looks like:
/// <summary>
/// Interface that propages path in order for a class to be displayed in a folder tree view
/// </summary>
internal interface IFolderTreeItem
{
/// <summary>
/// Gets the path folder tree where item located
/// </summary>
string FolderTreePath { get; }
/// <summary>
/// Gets or sets the folder where this item lives
/// </summary>
Folder ParentFolder { get; set; }
/// <summary>
/// Gets the name of the object that will be displayed
/// </summary>
string Name { get; }
/// <summary>
/// Gets or sets the list of items to display on the tree view
/// </summary>
IEnumerable AllItems { get; }
}
Note: Folder being an instance of IFolderTreeItem
Note: Im using AllItems property to bind to the HierarchicalDataTemplate.
Note: GetVirtualizingStackPanel() and GetVisualChild() will match Microsoft article.
Note: Not using any behaviors or attached properties (only drag/drop library but that didnt seem to be causing the issue).
Everything that I have stated before is working fine, the issue that I have, is that for some items that were selected from the search box results, the TreeViewItem selection is appearing and disappearing right after. see video example here: https://imgur.com/a/eadwBGg (focus on the bottom of the video).
My suspicion is that it is related to virtualization but I could be wrong, I could provide more code if needed, please advice on anything that could help, i have been battling this for a couple of days and i am out of ideas. The weirdest part is that for some items it will work, for some items it wont, which its why i think its an issue with virtualization.