다음을 통해 공유


방법: TreeView에서 TreeViewItem 찾기

TreeView 컨트롤은 계층적 데이터를 표시하는 편리한 방법을 제공합니다. TreeView가 데이터 원본에 바인딩된 경우 SelectedItem 속성을 사용하면 선택한 데이터 개체를 빠르게 검색할 수 있습니다. 일반적으로 기본 데이터 개체로 작업하는 것이 가장 좋지만 경우에 따라 TreeViewItem을 포함하는 데이터를 프로그래밍 방식으로 조작해야 할 수도 있습니다. 예를 들어 프로그래밍 방식으로 TreeViewItem을 확장하거나 TreeView에서 다른 항목을 선택해야 할 수 있습니다.

특정 데이터 개체가 포함된 TreeViewItem을 찾으려면 TreeView의 각 수준을 트래버스해야 합니다. TreeView의 항목을 가상화하여 성능을 향상시킬 수도 있습니다. 항목이 가상화될 수 있는 경우 TreeViewItem도 실현하여 데이터 개체가 포함되어 있는지 확인해야 합니다.

예제

설명

다음 예제에서는 TreeView에서 특정 개체를 검색하고 개체에 포함된 TreeViewItem을 반환합니다. 이 예제에서는 자식 항목을 검색할 수 있도록 각 TreeViewItem이 인스턴스화되도록 합니다. 이 예제는 TreeView가 가상화된 항목을 사용하지 않는 경우에도 작동합니다.

참고

다음 예제에서는 기본 데이터 모델에 관계없이 모든 TreeView 항목에 대해 작동하며 개체를 찾을 때까지 모든 TreeViewItem마다 검색합니다. 더 나은 성능의 또 다른 방법은 지정된 개체에 대한 데이터 모델을 검색하고 데이터 계층 구조 내에서 해당 위치를 추적한 다음 TreeView에서 해당하는 TreeViewItem을 찾는 것입니다. 그러나 더 나은 성능을 가진 해당 방법은 데이터 모델에 대한 지식이 필요하며 주어진 TreeView에 대해 일반화할 수 없습니다.

코드

/// <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

이전 코드는 BringIntoView라는 메서드를 노출하는 사용자 지정 VirtualizingStackPanel을 사용합니다. 다음 코드는 사용자 지정 VirtualizingStackPanel을 정의합니다.

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

다음 XAML은 사용자 지정 VirtualizingStackPanel을 사용하는 TreeView를 만드는 방법을 보여 줍니다.

<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>

참고 항목