Reputation: 11652
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.
Upvotes: 14
Views: 23141
Reputation: 557
NOTE: This anwer applies to WinUI3, but may also apply to WPF
This may be happening to you because you have the ListView
encapsulated into a ScrollView
, which handles the scroll.
<ScrollView ...>
<ListView ...>
...
</ListView>
</ScrollView>
Therefore, the ListView expands to the max height and it does not scroll to the specified item (since it technically is already visible, at least from the ListView's pov).
What you need to do is remove the ScrollView
and let the ListView
handle the scrolling, and then it will be able to properly detect that the given item is out of view, and scroll it into the view again.
<!-- There is no ScrollView, ListView will take care of overflows and scrollviews-->
<ListView ...>
...
</ListView>
Upvotes: 0
Reputation: 95
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>
Upvotes: 0
Reputation: 317
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
Upvotes: 0
Reputation: 171
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 -----
Upvotes: 0
Reputation: 133
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()
Upvotes: -2
Reputation: 61
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
Upvotes: 2
Reputation: 29
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);
Upvotes: -1
Reputation: 1576
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.
Upvotes: 4
Reputation: 1218
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.
Upvotes: 4
Reputation: 10214
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.
Upvotes: 11
Reputation: 69362
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]);
Upvotes: 21
Reputation: 30872
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);
Upvotes: 1