原文:WPF中TreeView.BringIntoView方法的替代方案
WPF中TreeView.BringIntoView方法的替代方案
周银辉
WPF中TreeView.BringIntoView()方法并不是那么地好用,不少时候会没有效果,这里有一个替代方案,调用SelectItem()方法可以展开并呈现TreeView上指定的Item:
public static class TreeViewHelper
{
/// <summary>
/// Expands all children of a TreeView
/// </summary>
/// <param name="treeView">The TreeView whose children will be expanded</param>
public static void ExpandAll(this TreeView treeView)
{
ExpandSubContainers(treeView);
}
/// <summary>
/// Expands all children of a TreeView or TreeViewItem
/// </summary>
/// <param name="parentContainer">The TreeView or TreeViewItem containing the children to expand</param>
private static void ExpandSubContainers(ItemsControl parentContainer)
{
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (currentContainer != null && currentContainer.Items.Count > 0)
{
//expand the item
currentContainer.IsExpanded = true;
//if the item's children are not generated, they must be expanded
if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
//once the children have been generated, expand those children's children then remove the event handler
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ExpandSubContainers(currentContainer);
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
else //otherwise the children have already been generated, so we can now expand those children
{
ExpandSubContainers(currentContainer);
}
}
}
}
/// <summary>
/// Searches a TreeView for the provided object and selects it if found
/// </summary>
/// <param name="treeView">The TreeView containing the item</param>
/// <param name="item">The item to search and select</param>
public static void SelectItem(this TreeView treeView, object item)
{
ExpandAndSelectItem(treeView, item);
}
/// <summary>
/// Finds the provided object in an ItemsControl's children and selects it
/// </summary>
/// <param name="parentContainer">The parent container whose children will be searched for the selected item</param>
/// <param name="itemToSelect">The item to select</param>
/// <returns>True if the item is found and selected, false otherwise</returns>
private static bool ExpandAndSelectItem(ItemsControl parentContainer, object itemToSelect)
{
//check all items at the current level
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
//if the data item matches the item we want to select, set the corresponding
//TreeViewItem IsSelected to true
if (item == itemToSelect && currentContainer != null)
{
currentContainer.IsSelected = true;
currentContainer.BringIntoView();
currentContainer.Focus();
//the item was found
return true;
}
}
//if we get to this point, the selected item was not found at the current level, so we must check the children
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
//if children exist
if (currentContainer != null && currentContainer.Items.Count > 0)
{
//keep track of if the TreeViewItem was expanded or not
bool wasExpanded = currentContainer.IsExpanded;
//expand the current TreeViewItem so we can check its child TreeViewItems
currentContainer.IsExpanded = true;
//if the TreeViewItem child containers have not been generated, we must listen to
//the StatusChanged event until they are
if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (ExpandAndSelectItem(currentContainer, itemToSelect) == false)
{
//The assumption is that code executing in this EventHandler is the result of the parent not
//being expanded since the containers were not generated.
//since the itemToSelect was not found in the children, collapse the parent since it was previously collapsed
currentContainer.IsExpanded = false;
}
//remove the StatusChanged event handler since we just handled it (we only needed it once)
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
else //otherwise the containers have been generated, so look for item to select in the children
{
if (ExpandAndSelectItem(currentContainer, itemToSelect) == false)
{
//restore the current TreeViewItem's expanded state
currentContainer.IsExpanded = wasExpanded;
}
else //otherwise the node was found and selected, so return true
{
return true;
}
}
}
}
//no item was found
return false;
}
}