WPF ListBox Scroll to end automatically

2019-02-02 02:52发布

问题:

In my application, I have a ListBox with items. The application is written in WPF.

How can I scroll automatically to the last added item? I want the ScrollViewer to be moved to the end of the list when new item has been added.

Is there any event like ItemsChanged? (I don't want to use the SelectionChanged event)

回答1:

Try this:

lstBox.SelectedIndex = lstBox.Items.Count -1;
lstBox.ScrollIntoView(lstBox.SelectedItem) ;

In your MainWindow, this will select and focus on last item on the list!



回答2:

Keep in mind that listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]); works only if you have no duplicate items. If you have items with the same contents it scrolls down to the first find.

Here is the solution I found:

ListBoxAutomationPeer svAutomation = (ListBoxAutomationPeer)ScrollViewerAutomationPeer.CreatePeerForElement(myListBox);

IScrollProvider scrollInterface = (IScrollProvider)svAutomation.GetPattern(PatternInterface.Scroll);
System.Windows.Automation.ScrollAmount scrollVertical = System.Windows.Automation.ScrollAmount.LargeIncrement;
System.Windows.Automation.ScrollAmount scrollHorizontal = System.Windows.Automation.ScrollAmount.NoAmount;
//If the vertical scroller is not available, the operation cannot be performed, which will raise an exception. 
if ( scrollInterface.VerticallyScrollable )
    scrollInterface.Scroll(scrollHorizontal, scrollVertical);


回答3:

The easiest way to do this:

if (VisualTreeHelper.GetChildrenCount(listView) > 0)
{
    Border border = (Border)VisualTreeHelper.GetChild(listView, 0);
    ScrollViewer scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
    scrollViewer.ScrollToBottom();
}

It is always working for ListView and ListBox controls. Attach this code to the listView.Items.SourceCollection.CollectionChanged event and you have fully automatic auto-scrolling behaviour.



回答4:

The best solution is to use the ItemCollection object inside the ListBox control this collection was specially designed to content viewers. It has a predefined method to select the last item and keep a cursor position reference....

myListBox.Items.MoveCurrentToLast();
myListBox.ScrollIntoView(myListBox.Items.CurrentItem);


回答5:

listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);



回答6:

A slightly different approach to those presented so far.

You could use the ScrollViewer ScrollChanged event and watch for the content of the ScrollViewer getting larger.

private void ListBox_OnLoaded(object sender, RoutedEventArgs e)
{
    var listBox = (ListBox) sender;

    var scrollViewer = FindScrollViewer(listBox);

    if (scrollViewer != null)
    {
        scrollViewer.ScrollChanged += (o, args) =>
        {
            if (args.ExtentHeightChange > 0)
                scrollViewer.ScrollToBottom();
        };
    }
}

This avoids some issues with the binding to the ListBox ItemsSource changing.

The ScrollViewer can also be found without making the assumption that the ListBox is using the default control template.

// Search for ScrollViewer, breadth-first
private static ScrollViewer FindScrollViewer(DependencyObject root)
{
    var queue = new Queue<DependencyObject>(new[] {root});

    do
    {
        var item = queue.Dequeue();

        if (item is ScrollViewer)
            return (ScrollViewer) item;

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(item); i++)
            queue.Enqueue(VisualTreeHelper.GetChild(item, i));
    } while (queue.Count > 0);

    return null;
}

Then attach this to the ListBox Loaded event:

<ListBox Loaded="ListBox_OnLoaded" />

This could be easily modified to be an attached property, to make it more general purpose.



回答7:

For me, the simplest working way was this: (without Binding)

 private void WriteMessage(string message, Brush color, ListView lv)
        {

            Dispatcher.BeginInvoke(new Action(delegate
            {
                ListViewItem ls = new ListViewItem
                {
                    Foreground = color,
                    Content = message
                };
                lv.Items.Add(ls);
                lv.ScrollIntoView(lv.Items[lv.Items.Count - 1]);
            }));
        }

Don't need to create classes or change the xaml, just write the messages with this method and it scroll automatically.

Calling just

myLv.Items.Add(ls);
myLv.ScrollIntoView(lv.Items[lv.Items.Count - 1]);

for exemple, don't work for me.



回答8:

You could try ListBox.ScrollIntoView() method, although there are some problems in some cases...

Here is an example from Tamir Khason: Auto scroll ListBox in WPF