14

I am trying to scroll into view so that the very last item in a vertical listivew is always showing, but ListView.ScrollIntoView() never works.

I have tried:

button1_Click(object sender, EventArgs e)
{

            activities.Add(new Activities()
            {
                Time = DateTime.Now,
                Message = message
            });

            ActivityList.ItemsSource = activities;

            // Go to bottom of ListView.
            ActivityList.SelectedIndex = ActivityList.Items.Count;
            ActivityList.ScrollIntoView(ActivityList.SelectedIndex);
}

I have also tried:

ActivityList.ScrollIntoView(ActivityList.Items.Count), and ActivityList.ScrollIntoView(ActivityList.Items.Count + 1); but nothing is working.

Please help. This is really annoying. And the documentation isn't very helpful in this case.

11 Answers 11

21

You're passing in the index when the method expects the item object. Try this to scroll to the selected item.

ActivityList.ScrollIntoView(ActivityList.SelectedItem);

If you want to scroll to the last item, you can use this

ActivityList.ScrollIntoView(ActivityList.Items[ActivityList.Items.Count - 1]);
9
  • 2
    Sorry, I forgot to mention that was the first thing I tried (as per the documentation). That does not work either.
    – jay_t55
    Commented Jun 5, 2013 at 14:33
  • 1
    SelectedIndex is also set beyond items listl. If, then it should be ActivityList.SelectedIndex = ActivityList.Items.Count - 1
    – dkozl
    Commented Jun 5, 2013 at 14:36
  • 3
    +1 @dkozl for the SelectedIndex value. Jase, if that still doesn't work, try calling ActivityList.UpdateLayout(); just before the ScrollIntoView method.
    – keyboardP
    Commented Jun 5, 2013 at 14:37
  • @keyboardP Thanks, LastOrDefault throws an error when compiling, saying it doesn't exist. I will try UpdateLayout() and see how that goes.
    – jay_t55
    Commented Jun 5, 2013 at 14:40
  • @keyboardP Ok, so far I have got: ActivityList.ScrollIntoView(ActivityList.SelectedItem); ActivityList.UpdateLayout(); but it still isn't working.
    – jay_t55
    Commented Jun 5, 2013 at 14:42
11

It's got something to do with the internal list representation, namely, the items are not yet in place when you call the ScrollIntoView(). After many attempts, this is what I finally came up with and this seems to work: attach two handlers to every ListBox/ListView:

<ListBox x:Name="YourList" ... Loaded="YourList_Loaded" SelectionChanged="YourList_SelectionChanged">

and

void YourList_Loaded(object sender, RoutedEventArgs e) {
  if (YourList.SelectedItem != null)
    YourList.ScrollIntoView(YourList.SelectedItem);
}

void YourList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  if (YourList.SelectedItem != null)
    YourList.ScrollIntoView(YourList.SelectedItem);
}

The second handler you can't do without. You can't call ScrollIntoView() immediately after you set the selected item, it's not yet ready. You have to allow the list to inform you when it has actually changed the item. The first handler depends on the circumstances, if you have problems with the selection showing up after the initial loading of the list contents, you might need it, too.

0
4

The problem with ActivityList.ScrollIntoView(ActivityList.Items[ActivityList.Items.Count - 1]); and similiar "solutions", at least from what I'm experiencing, is that when the ListBox contains the same item more than once, it jumps to the first it finds, i.e. lowest index. So, it doesn't care about the index of the item you input, it just looks for that item and picks the first.

I haven't yet found a solution to this, but the approach I'm taking right now is to Insert instead of Add items. In the original posters case I think that would result in:

activities.Insert(0, new Activities()
        {
            Time = DateTime.Now,
            Message = message
        });

or possibly:

ActivityList.Items.Insert(0, new Activities()
        {
            Time = DateTime.Now,
            Message = message
        });

Which causes every new entry to be inserted in the top, and thus ScrollIntoView doesn't even need to be called.

1
  • 2
    This was my problem, but I could solve it by wrapping my strings in a class. Commented Oct 20, 2017 at 9:00
4

Its an old post but since none of the answers here worked for my case I want to add my implementation. I found the solution here: [https://social.msdn.microsoft.com/Forums/vstudio/en-US/8a745550-4360-4b53-85f5-032f8f46e2cc/listview-scrollintoview-not-working-with-a-observablecollection?forum=wpf][1]

((INotifyCollectionChanged)lvLogs.Items).CollectionChanged += ListView_CollectionChanged;

private void ListView_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    ScrollToEnd(this.lvLogs);
}

private void ListView_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if ((e.Action == NotifyCollectionChangedAction.Add) && (e.NewItems != null))
            {
                try 
                {
                    ScrollToEnd(this.lvLogs);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString(), "Error updating list view", MessageBoxButton.OK, MessageBoxImage.Error);
                }

            }
        }

public void ScrollToEnd(ListView _ListView)
{
    ScrollViewer _ScrollViewer = GetDescendantByType(_ListView, typeof(ScrollViewer)) as ScrollViewer;
    _ScrollViewer.ScrollToEnd();
}

public 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;
}

For me it works perfect.

1
  • I tried them all and this is the only solution that worked for me.
    – henon
    Commented Feb 19, 2022 at 15:26
2

This is an old post but I had the same problem in a Windows Store app and found the following worked:

    private void PagesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        pageListView.UpdateLayout();
        pageListView.ScrollIntoView(pageListView.SelectedItem);
    }

Cheers, Paul

1

You are assigning the index of the item, and the list box expects the item itself. Try something like this:

 var activity = new Activities()
        {
            Time = DateTime.Now,
            Message = message
        };

 activities.Add(activity);

 ActivityList.ItemsSource = activities;
 ActivityList.ScrollIntoView(activity);
1
  • It's a ListView. And I was originally assigning the actual Object (ListView.SelectedItem) but that did not work. I forgot to put that in my question, as I have tried about a dozen things to try and get this to work. I'm actually working on a ListView here, not a ListBox, but MSDN links to ListBox page from the ListView.ScrollIntoView() link. As you can see from here: msdn.microsoft.com/en-us/library/…
    – jay_t55
    Commented Jun 5, 2013 at 14:37
0

I am not sure if this topic is still of interest, but I could not get the above options to work on my LISTBOX. I read somewhere that unequal item sizes also stopped the scroll working. I did get the below Powershell code to work; the 'UpdateLayout()' seemed to be vital:

$zItemsCount = $ListBox.Items.Count - 1
Write-Host -ForegroundColor Cyan "ListBox Items Count - 1:" $zItemsCount
(0..$zItemsCount) | ForEach-Object {
  $ListBox.SelectedIndex = $_
  $ListBox.UpdateLayout()
  $ListBox.ScrollIntoView($ListBox.SelectedItem)
  }#-EndOf: each item -----
0

In accordance with what keyboardP stated on Jun 5 '13 at 14:37 here: https://stackoverflow.com/a/16942650/2717521

if that still doesn't work, try calling ActivityList.UpdateLayout(); just before the ScrollIntoView method.

I had a simmilar problem between what I initially thought, identical DataGrids. First one would ScrollIntoView without any issues, second one refused to do so no matter what, unless I triggered UpdateLayout. It turns out that I was also grouping the second DataGrid:

        mycollection.GroupDescriptions.Clear();
        mycollection.GroupDescriptions.Add(new PropertyGroupDescription("PART"));
        datagrid_parts.ItemsSource = mycollection.View;

Apparently if your DataGrid is grouping, the ScrollViewer can not get correct "visual" space, because some visible areas stay inside Group as GroupItem. Refference: https://social.msdn.microsoft.com/Forums/vstudio/en-US/b809f5ae-08e7-415f-9e86-fc0086cb49e7/datagrid-scrollintoview-does-not-bring-given-item-into-view-when-using-virtualization-and-grouping?forum=wpf

0

One other reason that this can happen is because the item has been virtualized out by the ItemsPanel which by default is a VirtualizingStackPanel. If you don't require the virtualization because you only have a relatively small number of items then you can use the normal StackPanel as your ItemsPanel instead.

<ListBox>
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ListBox>
-1

The problem I faced was similar. I was adding programatically to a ListBox and needed the last item added to be visible. After tying various methods I found the following to be the simplest and the best

lstbox.Items.MoveCurrentToLast();
lstbox.ScrollIntoView(lstbox.Items.CurrentItem);
-2

Just call method UpdateLayout...

UpdateLayout();
ActivityList.ScrollIntoView(ActivityList.Items[ActivityList.Items.Count - 1]);

This will work...

Or more easier way...just compile app :) and it will work without UpdateLayot()

Not the answer you're looking for? Browse other questions tagged or ask your own question.