13

我有一个列表视图,其中填充了 8 列用户数据。用户可以选择启用自动刷新,这会导致 ListView 被清除并重新填充数据库中的最新数据。

问题是当项目被清除并重新填充时,可见区域会跳回列表的顶部。因此,如果我正在查看 2000 年的第 1000 项,则返回该项目非常不方便。

基本上,我要问的是,如何获得当前的滚动距离(x 和 y)然后恢复它们?

4

9 回答 9

15

我只是想为那些拼命尝试使用有问题的 ListView.TopItem 属性的人提供一些信息:

  1. 您必须在调用 ListView.EndUpdate 之后设置 TopItem 属性
  2. ListView 控件的项目必须将它们的 Text 属性设置为 String.Empty 以外的值,否则该属性将不起作用。
  3. 设置 ListView.TopItem 会间歇性地引发空引用异常。始终将这行代码保留在 Try...Catch 块中。

当然,这会导致ListView的滚动条跳到0又回到最上面的item的位置,很烦人。如果您找到解决此问题的方法,请更新此问题。

于 2010-01-24T12:45:05.643 回答
8

I used the following successfully:

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)
{ }
于 2012-10-11T01:10:30.237 回答
4

不久前我遇到了同样的问题,我最终实现了一个算法来比较模型和列表,所以我只添加/删除了改变的元素。这样,如果没有大的变化,列表就不会跳到开头。我想要实现的主要目标是效率(这样列表就不会闪烁)。

于 2009-03-09T13:58:26.250 回答
3

The TopItemIndex property on ListView is what you are looking for, however it has some confirmed bugs that should have been addressed in VS2010 release.. not sure (haven't checked).

Anyway, my workaround for making this work is to do this:

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

For some reason setting it directly does not update it, but setting it to the last item and then the one I want works reliably for me.

于 2011-12-06T19:32:51.077 回答
2

查看 ListView.TopItem 属性。它有一个索引,应该包含它在列表中的位置。在新列表中找到该索引,并将 TopItem 设置为该项目,它应该会自动滚动。

于 2009-03-09T14:05:28.527 回答
0

不幸的是,您将需要使用一些互操作来滚动到 ListView 中的确切位置。使用GetScrollInfo winapi 函数获取现有滚动位置,使用SendMessage滚动到该位置。

在 CodeProject 上的一篇名为Scrolling to a group with a ListView的文章中,可能会指导您找到解决方案。

于 2009-03-09T15:16:58.917 回答
0

I was having sort-of the same problem. I have a listView that I populate every 1/2 sec and when I set the TopItem to an ListItem whose index > visible items, then the list jumped between the topItem and back 2 spots.

So, to correct the problem, I set the TopIterm AFTER the call to 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];​
于 2010-03-25T19:22:47.817 回答
0

My solution to maintaining scroll position:

Form level variable:

private static int scrollSpot = 0;

Inside listview refresh (ie Timer,button) to store the current spot:

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

Inside refreshTheForm method to show the stored spot (put at very end of method):

if (scrollSpot <= 1)
{
     listView1.Items[scrollSpot].Selected = true;
}
else
{
     listView1.Items[scrollSpot - 2].Selected = true;
}
listView1.TopItem = listView1.SelectedItems[0]; 
于 2011-11-10T16:55:46.437 回答
0

In my tests, you did not even need the TopItem, although I used a int to save the selected item. Also TopItem throws an exception if you are using View.Tile or View.LargeIcon.

This code does not move the scroll bars:

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

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

listView1.EndUpdate();
于 2013-08-11T20:05:07.227 回答