Автопрокрутка списка в определенной ситуации - PullRequest
9 голосов
/ 27 января 2009

Как автоматически прокрутить список после добавления нового элемента, но только если полоса прокрутки находится внизу до добавления элемента?

Ответы [ 4 ]

10 голосов
/ 20 апреля 2012

Вы можете просто попробовать это:

listBox1.SelectedIndex = listBox1.Items.Count - 1;    
listBox1.SelectedIndex = -1;
9 голосов
/ 04 февраля 2009

Этот пример кода должен вам помочь. Я делал это много раз с TextBox, но потребовалось некоторое время, чтобы понять это для ListBox

Очевидно, это просто форма с кнопкой и списком. Изменить в соответствии с вашими потребностями:

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;
    }
}
1 голос
/ 26 октября 2010

Я придумала следующее решение, которое также будет работать для нарисованных владельцем списков с элементами переменной высоты.

Основная идея состоит в том, что он выясняет, прокручивается ли он до конца с помощью метода IndexToPoint и точки внизу списка, чтобы увидеть, находится ли последний элемент в этой позиции. Это имеет небольшой недостаток в том, что если последний элемент находится внизу, но не полностью виден, он все равно будет думать, что он прокручен вниз.

Он использует свойство TopIndex для прокрутки списка. Обратите внимание, что при установке TopIndex в качестве последнего элемента в списке он фактически не будет помещать его в верхнюю часть, если есть достаточно места для других элементов. В этом случае он поместит его внизу, что вам и нужно.

У него также есть дополнительный код, чтобы сохранить максимальное количество элементов в списке (определяемое в другом месте константой 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();
    }
0 голосов
/ 03 июня 2010

Я решил эту проблему, используя метод, подобный колитию, за исключением того, что потом обнаружил ошибку с одновременными обновлениями. Таким образом, есть член класса m_previousCount, который хранит количество элементов в ListBox до того, как произошло это обновление.

Я сделал это с ListView, но он должен работать так же и для ListBox.

В этом случае мой 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);
}
...