Jeff LaFay
Jeff LaFay

Reputation: 13360

WPF ListView: Custom Scrolling Behavior

Is there a way to keep a ListView scrolling vertically as data is updated via a binding to the ListView's ItemsSource property? I can't seem to find a property in either the ListView or its underlying GridView control to achieve the desired result. I would like it to scroll so that the newest information is always visible to the user. A scroll bar already appears as the rows add to the control but the most current row is not presented.

Should I consider using a different type of control that has auto-scroll capability? I only have two columns: one contains DateTimes and the other contains a simple string. I'm starting to think that my control choice may be too limited.

My XAML looks roughly like this:

<ListView ItemsSource="{Binding Updates}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Timestamp" DisplayMemberBinding="{Binding TimeStamp}"/>
            <GridViewColumn DisplayMemberBinding="{Binding UpdateString}" />
        </GridView>
    </ListView.View>
</ListView>

Upvotes: 1

Views: 2891

Answers (4)

Jeff LaFay
Jeff LaFay

Reputation: 13360

I found away to expose the ListView's ScrollViewer object to manipulate scrolling. My DataContext contains a collection derived from ObservableCollection and in the code behind I wired an eventhandler to the CollectionChanged event. I used a Decorator to expose the the first child of the ListView (the border) and it's child is the ScrollViewer. From there I call the ScrollToBottom() method to attain the desired behavior.

In the code behind for my View which contains the ListView, this is how it's coded

MyViewModel viewModel = DataContext as MyViewModel;

viewModel.MyCollection.CollectionChanged += (sender,e) =>
{
    if(e.NewItems != null)
    {
        Decorator border = VisualTreeHelper.GetChild(SummaryListView, 0) as Decorator;
        ScrollViewer scroll = border.Child as ScrollViewer;
        scroll.ScrollToBottom();
    }
};

I found how to expose the ScrollViewer on Anshulee's WindowsClient blog post.

Edit Previously, I was checking if the max number of items in the custom collection was met and then scrolled to bottom. That did not work so well with different screen resolutions because the app might display 15 lines in the ListView on one machine and 25 lines on another. So now I always have it scroll to bottom to bring the newest item into view.

Upvotes: 2

Flagbug
Flagbug

Reputation: 2103

A Listview has a method called ScrollIntoView(object item).

This will scroll the Listview to the position where the specified item is.

When you have a collection that is binded to the Listview, you can call this method when your collection has changed, something like:

private void myCollectionChanged()
{
    myListView.ScrollIntoView(myCollection.Last());
}

Upvotes: 0

Aaron McIver
Aaron McIver

Reputation: 24723

I'm pretty sure that a VirtualizingStackPanel is being used under the covers...which means that the items do no exist until they are needed. Therefore if you have 15000 items, rendering those 15000 items before needed makes no sense.

An easier approach may be to force a sort so the newest data remains on the top. It will then give you the appearance of pushing items down but they will be the oldest items so scrolling to see them doesn't matter unless the user initiates it.

Upvotes: 0

Daniel Perez
Daniel Perez

Reputation: 4332

you should use a scrollviewer object, it will automatically scroll when needed its contents (you can also specify if you always want horizontal or vertical scroll regardless where it's needed or not, and some other things as well)

<ScrollViewer>
   <GridView>
       <!-- gridview definition -->
   <GridView>
</ScrollViewer>

more info on MSDN

Upvotes: 1

Related Questions