Search code examples
c#wpflistbox

How to move to the top of a WPF Listbox?


I've got a scrollable ListBox in a .xaml file that's bound to some data - an observable collection in a view model.

When new data is added to the collection, that data gets added to the top of the ListBox. When the ListBox contains lots of data and I scroll down the ListBox, new data gets added to the top of it however I can't see it unless I use the scrollbar to scroll to the top. How can I automatically scroll to the top after each new item is added to the observable collection?

WPF code:

        <ListBox Grid.Row="2"
                 Grid.Column="0"
                 ItemsSource="{Binding BagItems}"
                 ItemTemplateSelector="{StaticResource BagItemTemplateSelector}"
                 Grid.ColumnSpan="5"
                 Foreground="{DynamicResource DarkerGreyBrush}"
                 Background="{DynamicResource LightestGreyBrush}"
                 FontWeight="Medium"
                 HorizontalContentAlignment="Stretch"
                 ItemContainerStyle="{DynamicResource ListBoxContainerStyle}"
                 SelectedItem="{Binding SelectedItem}" 
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 KeyDown="ListBox_KeyDown" ManipulationBoundaryFeedback="ListBox_ManipulationBoundaryFeedback">
            <i:Interaction.Behaviors>
                <behaviours:ListBoxSelectionModeOverrideBehaviour SupressKeyEvents="{Binding DialogController.DialogAvailable}" />
            </i:Interaction.Behaviors>
        </ListBox>

View model C# code:

if (shoppingBagItem != null)
{
    this.TryInvokeOnUiThread(() =>
    {
        this.BagItems.Insert(0, shoppingBagItem);
        this.SelectedItem = shoppingBagItem;
    });
}

Solution

  • The following Behavior class will automaticaly scroll the SelectedItem of a ListBox into view.

    public class perListBoxHelper : Behavior<ListBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
        }
    
        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
        }
    
        private static void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var listBox = sender as ListBox;
    
            if (listBox == null)
            {
                return;
            }
    
            Action action = () =>
            {
                var selectedItem = listBox.SelectedItem;
    
                if (selectedItem != null)
                {                    
                    listBox.ScrollIntoView(selectedItem);
                }
            };
    
            listBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
        }
    }
    

    Useage ...

    <ListBox ... >
        <i:Interaction.Behaviors>
            <vhelp:perListBoxScrollSelecionIntoViewBehavior />
        </i:Interaction.Behaviors>
    </ListBox>
    

    More details on my blog post.