1

I have a listbox that various items are added to. When a new item is added to the listbox, I need to scroll that item into view (basically scroll to the bottom).

I've tried the solution from How can I have a ListBox auto-scroll when a new item is added? and also from this blog post

However, neither solutions work because my listbox contains variable height items. If I hack my listbox items templates to have a fixed height instead, then it seems to work. Here is an example of one of my item templates:

<DataTemplate x:Key="StatusMessageTemplate">
    <Grid Grid.Column="1" VerticalAlignment="top" Margin="0,5,10,0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding Path=MessageText}" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" FontWeight="Bold" Foreground="{DynamicResource LightTextColorBrush}"/>
        <TextBlock Text="{Binding Path=created_at, StringFormat=t}" Style="{StaticResource Timestamp}" TextWrapping="Wrap"  HorizontalAlignment="Right" Grid.Row="0" Grid.Column="1"/>
    </Grid>
</DataTemplate>

How can I make the new items scroll into view regardless of their height?

Community
  • 1
  • 1
Matt Roberts
  • 26,371
  • 31
  • 103
  • 180
  • When you call ScrollIntoView(), are you sure you have a reference to the top-level FrameworkElement of your ListBox item, and not one of the child controls of the ListBoxItem? I'm not sure if that would make a difference, but if it does, it might be doing exactly what your asking. – Steve Aug 02 '13 at 10:20
  • Those 2 classes I tried work with the listitem, so I'm pretty sure that's not the issue. – Matt Roberts Aug 05 '13 at 08:54

2 Answers2

2

I need to scroll that item into view (basically scroll to the bottom).

ScrollIntoView behaves strange when the Listbox has variable height items.

If the sole purpose is to scroll to the bottom, you can access the Scrollviewer directly and scroll to the maximum possible offset as shown below.

var scrollViewer = GetDescendantByType(ListBoxChats, typeof(ScrollViewer)) as ScrollViewer;
scrollViewer.ScrollToVerticalOffset(Double.MaxValue);

public static Visual GetDescendantByType(Visual element, Type type)
{
    if (element == null)
    {
        return null;
    }
    if (element.GetType() == type)
    {
        return element;
    }
    Visual foundElement = null;
    if (element is FrameworkElement)
    {
        (element as FrameworkElement).ApplyTemplate();
    }
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
    {
        Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
        foundElement = GetDescendantByType(visual, type);
        if (foundElement != null)
        {
            break;
        }
    }
    return foundElement;
}

GetDescendantByType is an helper function written by punker76 @ another SO post

Community
  • 1
  • 1
Arctic
  • 807
  • 10
  • 22
1

I think I have found the problem. Variable height items are not calculated until displayed. So I add a timer to call the ScrollIntoView function. But even that didn't work well, so I used the VisualTreeHelper to find the ScrollViewer object and force it to the specific row. Here is the code.

     System.Windows.Threading.DispatcherTimer dTimer = new System.Windows.Threading.DispatcherTimer();
     dTimer.Interval = new TimeSpan(0, 0, 0, 0, 200); // 200 Milliseconds
     dTimer.Tick += new EventHandler(
        (seder, ea) =>
        {
           //Verses.ScrollIntoView(Verses.Items[itemIndex]);
           for (int i = 0; i < VisualTreeHelper.GetChildrenCount(Verses); i++)
           {
              DependencyObject depObj = VisualTreeHelper.GetChild(Verses, i);
              if (depObj is ScrollViewer)
              {
                 ScrollViewer sv = depObj as ScrollViewer;
                 sv.ScrollToVerticalOffset(itemIndex); // Zero based index
                 break;
              }
           }
           dTimer.Stop();
        });
     dTimer.Start();
Luis K
  • 11
  • 1