views:

137

answers:

2

How can one obtain the panel that is used within a TreeView? I've read that by default TreeView uses a VirtualizingStackPanel for this. When I look at a TreeView template, all I see is <ItemsPresenter />, which seems to hide the details of what panel is used.

Possible solutions:

1) On the treeview instance ("tv"), from code, do this: tv.ItemsPanel.

The problem is, this does not return a panel, but an ItemsPanelTemplate ("gets or sets the template that defines the panel that controls the layout of the items").

2) Make a TreeView template that explicitly replaces <ItemsPresenter /> with your own ItemsControl.ItemsPanel. I am providing a special template anyways, so this is fine in my scenario. Then give a part name to the panel that you place within that template, and from code you can obtain that part (i.e. the panel). The problem with this? see below.

(I am using a control named VirtualTreeView which is derived from TreeView, as is seen below):

<ControlTemplate x:Key="VirtualTreeViewTemplate" TargetType="{x:Type local:VirtualTreeView}">
    <Border>
        <local:VirtualScrollViewer 
             Style="{StaticResource VirtualScrollViewer}"
             x:Name="PART_VirtualScrollViewer"
                    CanContentScroll="True">
                <!-- instead of: <ItemsPresenter />, use following: -->
                <ItemsControl>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel
                            Name="PART_ItemsStackPanel"
                            IsItemsHost="True" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
        </local:VirtualScrollViewer>
    </Border>
</ControlTemplate>

[I stripped out all clutter here for visibility...]

The problem with this is: this immediately overrides any TreeView layout mechanism. Actually, you just get a blank screen, even when you have TreeViewItems filling the tree. Well, the reason I want to get a hold of the panel is to take some part in the MeaureOverride, but without going into all of that, I certainly do not want to rewrite the book of how to layout a treeview. I.e., doing this the step #2 way seems to invalidate the point of even using a TreeView in the first place.

Sorry if there is some confusion here, thanks for any help you can offer.

A: 

There is a class called "VisualTreeHelper" that allows you to get non-exposed child controls/elements of a control.

Here is a link to the GetChild method on that class which allows you to enumerate all the children, etc:

http://msdn.microsoft.com/en-us/library/system.windows.media.visualtreehelper.getchild.aspx

David
This does what I directly asked for, thanks much. Unfortunately, the larger thing I am after is not solved by this, which is, I am trying to then use MeasureOverride on that panel. I think I will have to derive a panel to override a method though. And that, unfortunately, brings me to step #2, where the TreeView doesn't seem to do any rendering at all on its own (empty screen). Thanks again though. I reworked the code you gave and am looking for a way to post it, which may be helpful to others.
A: 

The following is a supplement to the answer given by David above. This offers a way to get all of the Visual Children that are UIElements via an extension method. If you find this useful, please comment or vote, thanx.

public static class WPFXtensions
{
    static public IEnumerable<UIElement> GetDescendantUIElements(this UIElement uiElement)
    {
        int childrenCnt = VisualTreeHelper.GetChildrenCount(uiElement);
        for (int i = 0; i < childrenCnt; i++)
        {
            Visual childVisual = VisualTreeHelper.GetChild(uiElement, i) as Visual;

            if(childVisual == null)
                continue;   

            if (childVisual is UIElement)
            {
                yield return childVisual as UIElement;

                // note: by recursively calling within the loop, we walk the tree all the way down for each
                // UIElement encountered before moving to the next UIElement sibling. 
                foreach (UIElement e in GetDescendantUIElements(childVisual as UIElement))
                    yield return e;
            }
        }
    }
}

 // Use this then like this to retrieve an expected child StackPanel 

StackPanel sp1 = (StackPanel)this.GetDescendantUIElements()
            .First(uiElem => uiElem is StackPanel);

// Get all of this UIElement's descendants (for a diagnostic look); 
// In a run where this was a TreeView, 78 UIElements returned

List<UIElement> uiElements = this.GetDescendantUIElements().ToList();