This is a migrated thread and some comments may be shown as answers.

Bring children into view on expand node

4 Answers 255 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Aksel
Top achievements
Rank 1
Aksel asked on 25 Jan 2013, 08:42 AM
Hi,
I'm trying to replicate the way Windows Explorer works when you expand a node.
If you expand a node with child items, and those childred expand beyond the view, the view scrolls so that as many of the child nodes as possible are displayed.

Currently I have this code, which works the first time a node is expanded, but stops to work after contracting and expanding.

private void Tree_OnExpanded(object sender, RadRoutedEventArgs e)
{
var originalSource = e.OriginalSource
as RadTreeViewItem;
new Thread(() =>
{
    while (originalSource.ItemContainerGenerator.ContainerFromIndex(0) == null)
        Thread.Sleep(100);
        
    Dispatcher.BeginInvoke(new Action(() =>
    {
        tree.BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
        originalSource.BringIntoView();
        tree.BringIntoViewMode = BringIntoViewMode.Header;
    }));
 
 
}).Start();

Is there a better way to implement this behavior?

4 Answers, 1 is accepted

Sort by
0
Tina Stancheva
Telerik team
answered on 30 Jan 2013, 10:24 AM
Hello Aksel,

Please accept my apology for the delayed response.

In order to bring the expanded item to the top of the RadTreeView and display as many as possible of its children as possible, you can get the coordinates of the expanded item and scroll the RadTreeView.ScrollViewer to display this coordinates at the top. In order to do so, you'll need to transform the coordinates of the expanded RadTreeViewItem into the RadTreeView coordinate system:
Point p = item.TransformToVisual(tree).Transform(new Point(0, 0));
tree.ScrollViewer.ScrollToVerticalOffset(tree.ScrollViewer.VerticalOffset + p.Y);
and scroll to it.

I attached a sample solution demonstrating this approach. However, please note that I have disabled the RadTreeView animations as they delay the RadTreeView.ScrollViewer calculations. When you turn them on, the ScrollToVerticalOffset() logic will be executed before the ScrollViewer can fully calculate the real Height of the RadTreeView as the animations delay the realization of the RadTreeViewItem containers.

This is why if you have to turn the animations on, it's best to create custom animations and attach a behavior that will fire the tree.ScrollViewer.ScrollToVerticalOffset() logic after the animations are over. And in this case you should consider applying an animation for the scrolling logic as well to make it more fluent.

All the best,
Tina Stancheva
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Aksel
Top achievements
Rank 1
answered on 30 Jan 2013, 03:43 PM
I don't want the item to go to the top if it has room to expand.
Basically, I want the following to happen;

- If the last child item is visible, don't do anything.
- If the last child item is hidden, scroll down until the item is visible or until the parent is in the top position.

See the attached images
scroll1: We want to expand Item 5.
scroll2: It only has 5 children, so the last item is visible. No scroll is done.
scroll3: Now we expand Item 5.0, which has 20 children. It scrolls so far down it can, but stops when the parent is in the top position.
scroll4: Now if Item 5.0 only had 14 items, it would only scroll so far down that item 14 is visible, no further.

How should I implement this?
0
Aksel
Top achievements
Rank 1
answered on 31 Jan 2013, 10:00 AM
I have solved it for now:

private void Tree_OnExpanded(object sender, RadRoutedEventArgs e)
{
    var item = e.OriginalSource as RadTreeViewItem;
    if (item == null) return;
 
    var itenContainerWaiter = new ManualResetEvent(false);
     
    EventHandler handler = null;
    handler = (o, args) =>
      {
          var itemContainerGenerator = o as ItemContainerGenerator;
          if (itemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
              return;
 
          itemContainerGenerator.StatusChanged -= handler;
 
          itenContainerWaiter.Set();
      };
 
    item.ItemContainerGenerator.StatusChanged += handler;
 
    new Thread(() =>
       {
           itenContainerWaiter.WaitOne();
            
           Dispatcher.BeginInvoke(new Action(() =>
                {
                    tree.BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
                    item.BringIntoView();
                    tree.BringIntoViewMode = BringIntoViewMode.Header;
                }));
       }).Start();
}

This code waits untill the ItemContainerGenerator is done generating the child items, before displaying the items. For some reason, it did not work to have the BringIntoView() in the StatusChanged event. I had to split it into a separate thread.
0
Tina Stancheva
Telerik team
answered on 04 Feb 2013, 12:08 PM
Hi Aksel,

The ItemContainerGenerator.StatusChanged event is fired multiple times with a ContainerGenerator status and if you try too early to bring the item into view, it might not work as expected. I believe this is the reason why in your case moving the logic in another thread that is executed later works better.

Another approach that you can try is to use the RadTreeView.ItemPrepared event. It is fired for each RadTreeViewItem container after it is fully generated and initialized. In the handler of the event you can bring the item into view:
private void RadTreeView_ItemPrepared(object sender, RadTreeViewItemPreparedEventArgs e)
{
    if (e.PreparedItem.ParentItem!=null && e.PreparedItem.ParentItem.IsExpanded)
    {
        (sender as RadTreeView).BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
        e.PreparedItem.BringIntoView();
        (sender as RadTreeView).BringIntoViewMode = BringIntoViewMode.Header;
        expandStarted = false;
    }
}

All the best,
Tina Stancheva
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

Tags
TreeView
Asked by
Aksel
Top achievements
Rank 1
Answers by
Tina Stancheva
Telerik team
Aksel
Top achievements
Rank 1
Share this question
or