Show / Hide Table of Contents

Практическое руководство. Поиск элемента TreeViewItem в TreeView

TreeView Элемент управления предоставляет удобный способ отображения иерархических данных. Если ваш TreeView привязан к источнику данных SelectedItem свойство предоставляет удобный способ для быстрого извлечения выбранного объекта данных. Обычно лучше всего работать с основной объект данных, но иногда необходимо программно управлять данных, содержащий TreeViewItem. Например, может потребоваться программным образом развернуть TreeViewItem, или выберите другой элемент в TreeView.

Чтобы найти TreeViewItem , содержащий конкретный объект данных, необходимо пройти каждый уровень TreeView. Элементы в TreeView также могут быть виртуализированы для повышения производительности. В случае, когда виртуализации элементов, необходимо также реализовать TreeViewItem для проверки, содержит ли объект данных.

Пример

Описание

В следующем примере производится поиск TreeView для определенного объекта и возвращает, содержащий этот объект TreeViewItem. В примере проверяется, чтобы каждый TreeViewItem создается таким образом, чтобы поиска дочерних элементов. В этом примере также работает, если TreeView используются виртуализированные элементы.

Note

Следующий пример работает для любого TreeView, вне зависимости от базовой модели данных и выполняет каждый TreeViewItem пока не будет найден объект. Другой метод, имеющий более высокую производительность заключается поиск модели данных для указанного объекта, отслеживать его положение в иерархии данных и затем найти соответствующий TreeViewItem в TreeView. Тем не менее, метод, имеющий более высокую производительность требует знаний модели данных и не может быть обобщить для любого заданного 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;
}

Предыдущий код использует пользовательский VirtualizingStackPanel , предоставляет метод с именем BringIntoView. Следующий код определяет пользовательский VirtualizingStackPanel.

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

        this.BringIndexIntoView(index);
    }
}

Следующий XAML показан способ создания TreeView , использующего пользовательский VirtualizingStackPanel.

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

См. также

  • Повышение производительности элемента управления TreeView
Back to top Неофициальная документация по .NET на русском языке. Лицензия: CC-BY 4.0. Основано на документации по .NET с Microsoft Docs
Generated by DocFX