views:

737

answers:

4

When I expand items in my treeview so that scrolling is necessary, a scrollbar appears. However, it doesn't scroll down for the newly expanded branch of items - they get cropped by the bottom of the control. So as I continue expanding items at the bottom of the tree, I have to keep manually scrolling down to see the new children. Anyone have a suggestion for how make it automatically scroll to show the newly expanded items?

+1  A: 

On the TreeView, handle the TreeViewItem.Expanded event (you can do this at the TreeView level because of event bubbling). In the Expanded handler, call BringIntoView on the TreeViewItem that raised the event.

You may need a bit of trial and error to get hold of the TreeViewItem in your event handler code. I think (haven't checked) that the sender argument to your Expanded event handler will be the TreeView (since that's where the event handler is attached) rather than the TreeViewItem. And the e.Source or e.OriginalSource may be an element in the TreeViewItem's data template. So you may need to use VisualTreeHelper to walk up the visual tree to find the TreeViewItem. But if you use the debugger to inspect the sender and the RoutedEventArgs this should be trivial to figure out.

(If you're able to get this working and want to bundle it up so you don't have to attach the same event handler to every TreeView, it should be easy to encapsulate it as an attached behaviour which will allow you to apply it declaratively, including via a Style.)

itowlson
That actually works perfectly, I thought there would be a problem because I want to focus on the expanded child items, not the item that expanded, but it works exactly how I wanted it to. Thanks alot for the attached behaviors suggestion too, even better.
Jared
Actually this works for my treeview where I completely override the treeviewitem template, but doesn't work for my simpler treeview where I use the default treeviewitem template... don't know why
Jared
A: 

Thanks to itowlson's answer, here's the expanded event handler code that works for both of my trees

private static void Tree_Expanded(object sender, RoutedEventArgs e)
{
    // ignore checking, assume original source is treeviewitem
    var treeViewItem = (TreeViewItem)e.OriginalSource;

    var count = VisualTreeHelper.GetChildrenCount(treeViewItem);

    for (int i = count - 1; i >= 0; --i)
    {
        var childItem = VisualTreeHelper.GetChild(treeViewItem, i);
        ((FrameworkElement)childItem).BringIntoView();
    }

    // do NOT call BringIntoView on the actual treeviewitem - this negates everything
    //treeViewItem.BringIntoView();
}
Jared
A: 

Use a dependency property on an IsSelected trigger:

<Style TargetType="{x:Type TreeViewItem}">
 <Style.Triggers>
  <Trigger Property="IsSelected" Value="True">
    <Setter Property="commands:TreeViewItemBehavior.BringIntoViewWhenSelected" Value="True" />
  </Trigger>
</Style.Triggers>

Here's the code for the dependency property:

public static bool GetBringIntoViewWhenSelected(TreeViewItem treeViewItem)
{
  return (bool)treeViewItem.GetValue(BringIntoViewWhenSelectedProperty);
}

public static void SetBringIntoViewWhenSelected(TreeViewItem treeViewItem, bool value)
{
  treeViewItem.SetValue(BringIntoViewWhenSelectedProperty, value);
}

public static readonly DependencyProperty BringIntoViewWhenSelectedProperty =
    DependencyProperty.RegisterAttached("BringIntoViewWhenSelected", typeof(bool),
    typeof(TreeViewItemBehavior), new UIPropertyMetadata(false, OnBringIntoViewWhenSelectedChanged));

static void OnBringIntoViewWhenSelectedChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
  TreeViewItem item = depObj as TreeViewItem;
  if (item == null)
    return;

  if (e.NewValue is bool == false)
    return;

  if ((bool)e.NewValue)
    item.BringIntoView();
}
Mike
A: 

None of the above work

LLoyd Sheen