WinForms ListView, запоминание места прокрутки при перезагрузке

StackOverflow https://stackoverflow.com/questions/626315

Вопрос

У меня есть представление списка, которое я заполняю 8 столбцами пользовательских данных. У пользователя есть возможность включить автоматическое обновление, в результате чего ListView будет очищен и заполнен самыми последними данными из базы данных.

Проблема в том, что когда элементы очищаются и пополняются, видимая область возвращается наверх списка. Поэтому, если я смотрю на пункт 1000 2000 года, мне очень неудобно возвращаться к этому элементу.

По сути, я спрашиваю, как получить текущие расстояния прокрутки (x и y) и затем восстановить их?

Это было полезно?

Решение

Некоторое время назад у меня была такая же проблема, и в итоге я реализовал алгоритм сравнения модели со списком, поэтому я только добавил / удалил элементы, которые изменились. Таким образом, если не было массовых изменений, список не переходил к началу. И главное, чего я хотел добиться, - это эффективности (чтобы список не мигал).

Другие советы

Я просто хотел предоставить некоторую информацию тем, кто отчаянно пытается использовать ошибочное свойство ListView.TopItem:

<Ол>
  • Вы ДОЛЖНЫ установить свойство TopItem ПОСЛЕ вызова ListView.EndUpdate
  • Элементы элемента управления ListView ДОЛЖНЫ иметь свойство Text, установленное на что-то другое чем String.Empty, иначе свойство не будет работать.
  • Установка ListView.TopItem периодически вызывает исключение нулевой ссылки. Всегда держите эту строку кода внутри блока Try ... Catch.
  • Конечно, это заставит полосу прокрутки ListView перейти к 0 и вернуться к местоположению верхнего элемента, что раздражает. Пожалуйста, обновите этот вопрос, если вы найдете обходной путь для этой проблемы.

    Я успешно использовал следующее:

    int topItemIndex = 0;
    try
    {
         topItemIndex = listView1.TopItem.Index;
    }
    catch (Exception ex)
    { }
    listView1.BeginUpdate();
    listView1.Items.Clear();
    //CODE TO FILL LISTVIEW GOES HERE
    listView1.EndUpdate();
    try 
    { 
        listView1.TopItem = listView1.Items[topItemIndex];
    }
    catch (Exception ex)
    { }
    

    Свойство TopItemIndex в ListView - это то, что вы ищете, однако в нем есть некоторые подтвержденные ошибки, которые должны были быть исправлены в выпуске VS2010 ... не уверен (не проверял).

    В любом случае, мой обходной путь для выполнения этой работы заключается в следующем:

    listViewOutput.TopItemIndex = outputList.Count - 1;
    listViewOutput.TopItemIndex = myNewTopItemIndex;
    

    По какой-то причине его установка напрямую не обновляет его, но установка последнего элемента, а затем того, который я хочу, надежно работает для меня.

    Посмотрите на свойство ListView.TopItem. У него есть индекс, который должен содержать свою позицию в списке. Найдите этот индекс в новом списке и установите TopItem для этого элемента, и он должен выполнить прокрутку автоматически.

    К сожалению, вам понадобится некоторое взаимодействие для прокрутки до точной позиции в ListView. Используйте GetScrollInfo функцию winapi для получения существующей позиции прокрутки и SendMessage для прокрутки до позиции.

    В статье о CodeProject есть статья Прокрутка в группу с помощью ListView это может привести вас к решению.

    Мое решение для поддержания позиции прокрутки:

    Переменная уровня формы:

    private static int scrollSpot = 0;
    

    Внутри обновления списка (т. е. таймер, кнопка), чтобы сохранить текущую точку:

    scrollSpot = this.listView1.TopItem.Index;
    refreshTheForm();
    

    Внутри метода refreshTheForm, чтобы показать сохраненную область (помещается в самом конце метода):

    if (scrollSpot <= 1)
    {
         listView1.Items[scrollSpot].Selected = true;
    }
    else
    {
         listView1.Items[scrollSpot - 2].Selected = true;
    }
    listView1.TopItem = listView1.SelectedItems[0]; 
    

    У меня была такая же проблема. У меня есть listView, который я заполняю каждые 1/2 секунды, и когда я устанавливаю TopItem на ListItem, чей индекс > видимые элементы, затем список перемещается между topItem и двумя задними точками.

    Итак, чтобы исправить проблему, я установил TopIterm ПОСЛЕ вызова EndUpdate.

    lvB.EndUpdate();
    lvI.EndUpdate();
    lvR.EndUpdate();
    
    if (lstEntryInts.Items.Count > 0)
        lstEntryInts.TopItem = lstEntryInts.Items[iTopVisIdx];
    if (lstEntryBools.Items.Count > 0)
        lstEntryBools.TopItem = lstEntryBools.Items[iTopVisIdx];
    if (lstEntryReals.Items.Count > 0)
        lstEntryReals.TopItem = lstEntryReals.Items[iTopVisIdx];​
    

    В моих тестах вам даже не понадобился TopItem, хотя я использовал int для сохранения выбранного элемента. Также TopItem генерирует исключение, если вы используете View.Tile или View.LargeIcon.

    Этот код не перемещает полосы прокрутки:

    listView1.BeginUpdate();
    listView1.Items.Clear();
    
    // loop through your add routine
    listView1.Items.Add(lvi);
    
    listView1.EndUpdate();
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top