Bring children into view on expand node

5 posts, 0 answers
  1. Aksel
    Aksel avatar
    11 posts
    Member since:
    Jun 2012

    Posted 25 Jan 2013 Link to this post

    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?
  2. Tina Stancheva
    Admin
    Tina Stancheva avatar
    3298 posts

    Posted 30 Jan 2013 Link to this post

    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.

  3. UI for WPF is Visual Studio 2017 Ready
  4. Aksel
    Aksel avatar
    11 posts
    Member since:
    Jun 2012

    Posted 30 Jan 2013 Link to this post

    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?
  5. Aksel
    Aksel avatar
    11 posts
    Member since:
    Jun 2012

    Posted 31 Jan 2013 Link to this post

    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.
  6. Tina Stancheva
    Admin
    Tina Stancheva avatar
    3298 posts

    Posted 04 Feb 2013 Link to this post

    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.

Back to Top
UI for WPF is Visual Studio 2017 Ready