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

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

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

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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)
{ }

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 6

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

В статье о CodeProject с именем Прокрутка к группе со списком ListView, которая может помочь вам решить проблему.

Ответ 7

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

Итак, чтобы исправить проблему, я установил 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];​

Ответ 8

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

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

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]; 

Ответ 9

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

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

listView1.BeginUpdate();
listView1.Items.Clear();

// loop through your add routine
listView1.Items.Add(lvi);

listView1.EndUpdate();