Let ListView scroll to selected item
Asked Answered
C

2

9

I have a WinRT/C#/XAML app with a view that has a vertical ListView of items. Depending on the amount of items the ListView shows a vertical scrollbar. Here's the XAML definition:

<UserControl.Resources>
    <CollectionViewSource
        x:Name="myViewSource"
        Source="{Binding myViewModel.Items}" />
</UserControl.Resources>
...
<ListView
    x:Name="myListView"
    ItemsSource="{Binding Source={StaticResource myViewSource}}"
    SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
</ListView>

Now everytime I navigate to this view, the selected item of the ListView is chosen by setting the databound SelectedItem property in the view model from code behind (OnNavigatedTo). My problem: the ListView doesn't scroll automatically to this selected item. The scrollbar remains at the top of the ListView and the user has to scroll manually to see the selected item.

I tried to execute myListView.ScrollIntoView(MyViewModel.SelectedItem); after setting the SelectedItem in the code behind (in OnNavigatedTo), but it doesn't work. The scrollbar remains at the top.

I'm aware of this thread on SO: Scroll WinRT ListView to particular group . This seems to be a similar problem. But when I walk the visual tree of the ListView manually or with the WinRT XAML Toolkit, it doesn't find a ScrollViewer (returns null instead).

Clyster answered 24/8, 2012 at 11:23 Comment(3)
Are you waiting for the ListView to load (be added to the visual tree) before you try scrolling?Condillac
I'm calling ScrollIntoView() in OnNavigatedTo(). I thought this would be the right place and the ListView should have been loaded. Or may I be wrong?Clyster
I think OnNavigatedTo occurs before the control is loaded...Condillac
C
19

Thanks to Filip I noticed that calling ScrollIntoView() in OnNavigatedTo() was too early, because the ListView control is not loaded yet in this place.

The first solution idea was to bind the Loaded event of the ListView:

myListView.Loaded += (s, e) => 
    myListView.ScrollIntoView(MyViewModel.SelectedItem);

Unfortunately that causes a nasty visual effect, where current ListView items overlap with the selected item for parts of a second, before everything is rearranged well.

The final solution I found is to call ScrollIntoView() asynchronously via the Dispatcher of the view:

myListView.Loaded += (s, e) => Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    () => myListView.ScrollIntoView(MyViewModel.SelectedItem));

With this solution the layouting works fine.

Clyster answered 27/8, 2012 at 7:18 Comment(2)
I thing, that you have small syntax error in your solution - semicolon after this myListView.ScrollIntoView(MyViewModel.SelectedItem) lambda expression body.Simitar
In my case myListView.UpdateLayout() before myListView.ScrollIntoView() also helpedZsazsa
M
5

I had a similar need and resolved it in a slightly different manner. I subscribed to the SelectionChangedEvent from the ListView and performed the scrolling within the handler.

XAML:

<ListView x:Name="myListView" SelectionChanged="myListView_SelectionChanged" ...>
</ListView>

Code:

private void myListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    myListView.ScrollIntoView(myListView.SelectedItem);
}
Mountford answered 10/10, 2016 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.