Compartilhar via


Como localizar um TreeViewItem em um TreeView

O controle TreeView fornece uma maneira conveniente de exibir dados hierárquicos. Se o TreeView estiver associado a uma fonte de dados, a propriedade SelectedItem fornecerá uma maneira conveniente para você recuperar rapidamente o objeto de dados selecionado. Normalmente, é melhor trabalhar com o objeto de dados subjacente, mas, às vezes, talvez seja necessário manipular programaticamente os dados que contêm TreeViewItem. Por exemplo, talvez seja necessário expandir programaticamente o TreeViewItemou selecionar um item diferente no TreeView.

Para encontrar um TreeViewItem que contenha um objeto de dados específico, você deve percorrer cada nível do TreeView. Os itens em um TreeView também podem ser virtualizados para melhorar o desempenho. No caso em que os itens podem ser virtualizados, você também deve realizar uma TreeViewItem para verificar se ela contém o objeto de dados.

Exemplo

Descrição

O exemplo a seguir pesquisa um TreeView de um objeto específico e retorna o objeto que contém TreeViewItem. O exemplo garante que cada TreeViewItem seja instanciado para que seus subitens possam ser pesquisados. Este exemplo também funcionará se o TreeView não usar itens virtualizados.

Nota

O exemplo a seguir funciona para qualquer TreeView, independentemente do modelo de dados subjacente, e pesquisa cada TreeViewItem até que o objeto seja encontrado. Outra técnica que tem melhor desempenho é pesquisar o modelo de dados para o objeto especificado, manter o controle de sua localização dentro da hierarquia de dados e, em seguida, encontrar o TreeViewItem correspondente no TreeView. No entanto, a técnica que tem melhor desempenho requer conhecimento do modelo de dados e não pode ser generalizada para determinado TreeView.

Código

/// <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 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 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;
}
''' <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 Function GetTreeViewItem(ByVal container As ItemsControl,
                                 ByVal item As Object) As TreeViewItem

    If container IsNot Nothing Then
        If container.DataContext Is item Then
            Return TryCast(container, TreeViewItem)
        End If

        ' Expand the current container 
        If TypeOf container Is TreeViewItem AndAlso
           Not DirectCast(container, TreeViewItem).IsExpanded Then

            container.SetValue(TreeViewItem.IsExpandedProperty, True)
        End If

        ' Try to generate the ItemsPresenter and the ItemsPanel. 
        ' by calling ApplyTemplate. Note that in the 
        ' virtualizing case, even if IsExpanded = true, 
        ' we still need to do this step in order to 
        ' regenerate the visuals because they may have been virtualized away. 
        container.ApplyTemplate()

        Dim itemsPresenter As ItemsPresenter =
            DirectCast(container.Template.FindName("ItemsHost", container), ItemsPresenter)

        If itemsPresenter IsNot Nothing Then
            itemsPresenter.ApplyTemplate()
        Else
            ' The Tree template has not named the ItemsPresenter, 
            ' so walk the descendents and find the child. 
            itemsPresenter = FindVisualChild(Of ItemsPresenter)(container)

            If itemsPresenter Is Nothing Then
                container.UpdateLayout()

                itemsPresenter = FindVisualChild(Of ItemsPresenter)(container)
            End If
        End If

        Dim itemsHostPanel As Panel =
            DirectCast(VisualTreeHelper.GetChild(itemsPresenter, 0), Panel)


        ' Do this to ensure that the generator for this panel has been created. 
        Dim children As UIElementCollection = itemsHostPanel.Children

        Dim virtualizingPanel As MyVirtualizingStackPanel =
            TryCast(itemsHostPanel, MyVirtualizingStackPanel)


        For index As Integer = 0 To container.Items.Count - 1

            Dim subContainer As TreeViewItem

            If virtualizingPanel IsNot Nothing Then

                ' Bring the item into view so 
                ' that the container will be generated. 
                virtualizingPanel.BringIntoView(index)

                subContainer =
                    DirectCast(container.ItemContainerGenerator.ContainerFromIndex(index), 
                        TreeViewItem)
            Else
                subContainer =
                    DirectCast(container.ItemContainerGenerator.ContainerFromIndex(index), 
                        TreeViewItem)

                ' Bring the item into view to maintain the 
                ' same behavior as with a virtualizing panel. 
                subContainer.BringIntoView()
            End If

            If subContainer IsNot Nothing Then

                ' Search the next level for the object.
                Dim resultContainer As TreeViewItem =
                    GetTreeViewItem(subContainer, item)

                If resultContainer IsNot Nothing Then
                    Return resultContainer
                Else
                    ' The object is not under this TreeViewItem
                    ' so collapse it.
                    subContainer.IsExpanded = False
                End If
            End If
        Next
    End If

    Return Nothing
End Function

''' <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 Function FindVisualChild(Of T As Visual)(ByVal visual As Visual) As T

    For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(visual) - 1

        Dim child As Visual = DirectCast(VisualTreeHelper.GetChild(visual, i), Visual)

        If child IsNot Nothing Then

            Dim correctlyTyped As T = TryCast(child, T)
            If correctlyTyped IsNot Nothing Then
                Return correctlyTyped
            End If

            Dim descendent As T = FindVisualChild(Of T)(child)
            If descendent IsNot Nothing Then
                Return descendent
            End If
        End If
    Next

    Return Nothing
End Function

O código anterior se baseia em um VirtualizingStackPanel personalizado que expõe um método chamado BringIntoView. O código a seguir define o VirtualizingStackPanelpersonalizado.

public class MyVirtualizingStackPanel : VirtualizingStackPanel
{
    /// <summary>
    /// Publically expose BringIndexIntoView.
    /// </summary>
    public void BringIntoView(int index)
    {

        this.BringIndexIntoView(index);
    }
}
Public Class MyVirtualizingStackPanel
    Inherits VirtualizingStackPanel
    ''' <summary> 
    ''' Publically expose BringIndexIntoView. 
    ''' </summary> 
    Public Overloads Sub BringIntoView(ByVal index As Integer)

        Me.BringIndexIntoView(index)
    End Sub
End Class

O XAML a seguir mostra como criar um TreeView que usa o VirtualizingStackPanelpersonalizado.

<TreeView VirtualizingStackPanel.IsVirtualizing="True">

  <!--Use the custom class MyVirtualizingStackPanel
      as the ItemsPanel for the TreeView and
      TreeViewItem object.-->
  <TreeView.ItemsPanel>
    <ItemsPanelTemplate>
      <src:MyVirtualizingStackPanel/>
    </ItemsPanelTemplate>
  </TreeView.ItemsPanel>
  <TreeView.ItemContainerStyle>
    <Style TargetType="TreeViewItem">
      <Setter Property="ItemsPanel">
        <Setter.Value>
          <ItemsPanelTemplate>
            <src:MyVirtualizingStackPanel/>
          </ItemsPanelTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </TreeView.ItemContainerStyle>
</TreeView>

Consulte também