WinForms ListView, запоминание места прокрутки при перезагрузке
-
06-07-2019 - |
Вопрос
У меня есть представление списка, которое я заполняю 8 столбцами пользовательских данных. У пользователя есть возможность включить автоматическое обновление, в результате чего ListView будет очищен и заполнен самыми последними данными из базы данных.
Проблема в том, что когда элементы очищаются и пополняются, видимая область возвращается наверх списка. Поэтому, если я смотрю на пункт 1000 2000 года, мне очень неудобно возвращаться к этому элементу.
По сути, я спрашиваю, как получить текущие расстояния прокрутки (x и y) и затем восстановить их?
Решение
Некоторое время назад у меня была такая же проблема, и в итоге я реализовал алгоритм сравнения модели со списком, поэтому я только добавил / удалил элементы, которые изменились. Таким образом, если не было массовых изменений, список не переходил к началу. И главное, чего я хотел добиться, - это эффективности (чтобы список не мигал).
Другие советы
Я просто хотел предоставить некоторую информацию тем, кто отчаянно пытается использовать ошибочное свойство ListView.TopItem:
<Ол>Конечно, это заставит полосу прокрутки 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();