方法: TreeView で TreeViewItem を検索する
TreeView コントロールは、階層データを表示する便利な方法を提供します。 TreeView がデータ ソースにバインドされている場合、SelectedItem プロパティを使用すると、選択したデータ オブジェクトをすばやく取得するのに便利です。 通常、基になるデータ オブジェクトを操作することをお勧めしていますが、場合によっては、TreeViewItemを含むデータをプログラムで操作する必要があります。 たとえば、TreeViewItemをプログラムで展開したり、TreeViewで別の項目を選択したりする必要がある場合があります。
特定のデータ オブジェクトを含む TreeViewItem を検索するには、TreeViewの各レベルを走査する必要があります。 TreeView 内の項目は、パフォーマンスを向上させるために仮想化することもできます。 項目が仮想化される可能性がある場合は、データ オブジェクトが含まれているかどうかを確認する TreeViewItem も実現する必要があります。
例
説明
次の例では、特定のオブジェクトの TreeView を検索し、オブジェクトを含む TreeViewItemを返します。 この例では、子項目を検索できるように、各 TreeViewItem がインスタンス化されていることを確認します。 この例は、TreeView が仮想化された項目を使用しない場合にも機能します。
手記
次の例は、基になるデータ モデルに関係なく、任意の TreeViewに対して機能し、オブジェクトが見つかるまですべての TreeViewItem を検索します。 パフォーマンスを向上させるもう 1 つの手法は、指定されたオブジェクトのデータ モデルを検索し、データ階層内でその場所を追跡し、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>
関連項目
- TreeView のパフォーマンスを向上させる
.NET Desktop feedback