WPF ListBox Scroll to end automatically

2019-02-02 02:51发布

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)

8条回答
放我归山
2楼-- · 2019-02-02 03:45

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);
查看更多
【Aperson】
3楼-- · 2019-02-02 03:49

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.

查看更多
登录 后发表回答