C# 特定情况下自动滚动列表框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/485196/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 05:15:24  来源:igfitidea点击:

Autoscrolling a Listbox under a certain situation

c#listboxscroll

提问by

How to autoscroll a Listbox after adding a new item, but only if the scrollbar is at the bottom before the item is added?

如何在添加新项目后自动滚动列表框,但前提是在添加项目之前滚动条位于底部?

回答by colithium

This sample code should help you out. I've done this many times with a TextBox but it took awhile to figure it out for a ListBox

此示例代码应该可以帮助您。我已经用 TextBox 做过很多次了,但花了一段时间才弄明白 ListBox

Obviously, it's just a Form with a Button and a ListBox. Modify to fit your needs:

显然,它只是一个带有按钮和列表框的表单。修改以满足您的需求:

private void button1_Click(object sender, EventArgs e)
{
    listBox1.Items.Add("Some Text " + listBox1.Items.Count.ToString());

    //The max number of items that the listbox can display at a time
    int NumberOfItems = listBox1.ClientSize.Height / listBox1.ItemHeight;

    if (listBox1.TopIndex == listBox1.Items.Count - NumberOfItems - 1)
    {
        //The item at the top when you can just see the bottom item
        listBox1.TopIndex = listBox1.Items.Count - NumberOfItems + 1;
    }
}

回答by metao

I solved this problem using a similar method to colithium, except I then realised there was a bug with concurrent updates. So there is a class member called m_previousCount which stores the number of items in the ListBox before this update happened.

我使用与 colithium 类似的方法解决了这个问题,但后来我意识到并发更新存在错误。因此,有一个名为 m_previousCount 的类成员,用于存储此更新发生之前 ListBox 中的项目数。

I did this with a ListView, but it should work the same for a ListBox.

我用 ListView 做到了这一点,但它应该对 ListBox 起作用。

In this case, my listView1 is content-bound to listViewModel1.Entries.

在这种情况下,我的 listView1 内容绑定到 listViewModel1.Entries。

private void EventMessageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    listView1.Dispatcher.BeginInvoke(DispatcherPriority.Background, new ScrollToLastItemDelegate(ScrollToLastItem)); 
}

/// <summary>
/// Scroll to last item, unless you have scrolled up the list
/// </summary>
private void ScrollToLastItem()
{
    // Get scrollviewer
    Decorator border = System.Windows.Media.VisualTreeHelper.GetChild(listView1, 0) as Decorator;
    ScrollViewer scrollViewer = border.Child as ScrollViewer;

    double vo = scrollViewer.VerticalOffset;

    // assume they are all the same height
    ListBoxItem lbi = listView1.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;

    //The max number of items that the listbox can display at a time
    double NumberOfItemsOnScreen = listView1.ActualHeight / lbi.ActualHeight;

    // use previousCount in case we get multiple updates in one go
    if (m_previousCount > NumberOfItemsOnScreen) // scrollbar should be active
    {
        if (vo < (m_previousCount - NumberOfItemsOnScreen)) // you're not at the bottom
        {
            return; // don't scroll to the last item
        }
    }

    m_previousCount = listView1.Items.Count;

    // scroll to the last item
    listView1.SelectedItem = listView1.Items.GetItemAt(listViewModel1.Entries.Count - 1);

    listView1.ScrollIntoView(listView1.SelectedItem);
}

回答by dunni

I came up with the following solution which will also work for owner drawn listboxes with variable height items.

我想出了以下解决方案,它也适用于具有可变高度项目的所有者绘制的列表框。

The basic idea is that it figures out if it's scrolled to the bottom by using the IndexToPoint method and a point at the bottom of the listbox to see if the last item is at that position. This has a slight flaw in that if the last item is at the bottom but not fully visible it will still think it's scrolled to the bottom.

基本思想是它通过使用 IndexToPoint 方法和列表框底部的一个点来确定它是否滚动到底部,以查看最后一项是否在该位置。这有一个小缺陷,如果最后一个项目在底部但不完全可见,它仍然会认为它已滚动到底部。

It uses the TopIndex property to scroll the list box. Note that when setting the TopIndex to be the last item in the listbox it won't actually put it at the top if there is plenty of room for other items. In that case it will put it at the bottom, which is what you want.

它使用 TopIndex 属性滚动列表框。请注意,当将 TopIndex 设置为列表框中的最后一项时,如果其他项有足够的空间,它实际上不会将其放在顶部。在这种情况下,它会将它放在底部,这就是您想要的。

It also has some extra code to keep the number of items in the list to a maximum amount (defined elsewhere by the constant MAX_LISTBOX_ITEMS) by deleting the top one after it gets full. When it does this it works out where it will need to scroll the list to to keep the same items showing even after one is deleted. If you don't care about controlling the number of items added to the listbox you should be able to remove the middle if clause from the code and any mentions of the scrollToIndex variable.

它还有一些额外的代码,通过在列表中的项目满后删除顶部的项目,将列表中的项目数量保持在最大数量(在其他地方由常量 MAX_LISTBOX_ITEMS 定义)。当它这样做时,它会计算出需要滚动列表的位置,以保持相同的项目即使在删除后也能显示。如果您不关心控制添加到列表框的项目数,您应该能够从代码中删除中间的 if 子句以及任何对 scrollToIndex 变量的提及。

private void AddItemToListBox(ListBox listBox, object newItem)
    {
        int scrollToIndex = -1;
        bool scrollToEnd = false;

        //Work out if we should scroll to the end after adding a new item
        int lastIndex = listBox.IndexFromPoint(4, listBox.ClientSize.Height - 4);
        if ((lastIndex < 0) || (lastIndex == listBox.Items.Count - 1))
        {
            scrollToEnd = true;
        }

        //To prevent listbox jumping about as we delete and scroll
        listBox.BeginUpdate();

        //Work out if we have too many items and have to delete
        if (listBox.Items.Count >= MAX_LISTBOX_ITEMS)
        {
            //If deleting an item, and not scrolling to the end when new item added anyway,
            //then we will need to scroll to keep in the same place, work out new scroll index
            if (!scrollToEnd)
            {
                scrollToIndex = listBox.TopIndex - 1;
                if (scrollToIndex < 0)
                {
                    scrollToIndex = 0;
                }
            }

            //Remove top item
            listBox.Items.Remove(listBox.Items[0]);
        }

        //Add new item
        listBox.Items.Add(newItem);

        //Scroll if necessary
        if (scrollToEnd)
        {
            listBox.TopIndex = listBox.Items.Count - 1;
        }
        else if (scrollToIndex >= 0)
        {
            listBox.TopIndex = scrollToIndex;
        }

        listBox.EndUpdate();
    }

回答by scrat789

You can just try it :

你可以试试看:

listBox1.SelectedIndex = listBox1.Items.Count - 1;    
listBox1.SelectedIndex = -1;