C# WinForms ListView,在重新加载时记住滚动的位置

C# WinForms ListView,在重新加载时记住滚动的位置,c#,winforms,listview,.net-2.0,listviewitem,C#,Winforms,Listview,.net 2.0,Listviewitem,我有一个列表视图,我正在填充8列用户数据。用户可以选择启用自动刷新,这会导致清除ListView并使用数据库中的最新数据重新填充 问题是,当项目被清除并重新填充时,可见区域会跳回列表的顶部。所以,如果我看的是2000年的第1000项,回到那一项是非常不方便的 基本上,我要问的是,如何获得当前的滚动距离(x和y),然后恢复它们?我不久前遇到了同样的问题,我最终实现了一个算法来比较模型和列表,所以我只添加/删除了更改的元素。这样一来,如果没有重大变化,列表就不会跳到开头。我想实现的主要目标是提高效率

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

问题是,当项目被清除并重新填充时,可见区域会跳回列表的顶部。所以,如果我看的是2000年的第1000项,回到那一项是非常不方便的


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

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

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

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


在一篇名为CodeProject的文章中,可能会引导您找到解决方案。

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

  • 调用ListView.EndUpdate后必须设置TopItem属性
  • ListView控件的项必须将其文本属性设置为其他属性 大于String.Empty,否则该属性将不起作用
  • 设置ListView.TopItem会间歇性引发空引用异常。始终将这行代码保存在Try…Catch块中

  • 当然,这会导致ListView的滚动条跳转到0并返回到顶部项目的位置,这很烦人。如果找到解决此问题的方法,请更新此问题。

    我也遇到过类似的问题。我有一个listView,每1/2秒填充一次,当我将TopItem设置为其索引>可见项的ListItem时,列表在TopItem和后面2个点之间跳转

    因此,为了纠正这个问题,我在调用EndUpdate之后设置了TopIterm

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

    我维护滚动位置的解决方案:

    表单级别变量:

    private static int scrollSpot = 0;
    
    内部listview刷新(即计时器、按钮)以存储当前点:

    scrollSpot = this.listView1.TopItem.Index;
    refreshTheForm();
    
    在RefreshForm方法中显示存储的点(放在方法的末尾):


    if(scrollSpotListView上的TopItemIndex属性是您要查找的,但是它有一些已确认的错误,应该在VS2010版本中解决。不确定(尚未检查)

    无论如何,我的解决办法是:

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

    由于某些原因,直接设置它不会更新它,但将它设置为最后一项,然后设置为我想要的项对我来说是可靠的。

    我成功地使用了以下选项:

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

    在我的测试中,您甚至不需要TopItem,尽管我使用了int来保存所选项目。如果您使用的是View.Tile或View.LargeIcon,TopItem也会引发异常

    此代码不会移动滚动条:

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

    这就是我所做的。我在第一个子项中添加了一个标记,然后用它来进行比较,并仅在需要时进行更新。ListView.TopItem似乎不起作用。我认为它可能与排序有关,所以我禁用了它,但当我将TopItem设置为一个项并在之后立即检查属性时,它没有被更改(更改到另一个项目,而不是我指定的项目)。你知道吗?我发现在分配到TopItem(即,它滚动到我请求的项目上方)时出现了一个错误,但如果我调用它两次,它就会工作。Buggy,你说?:-)它当然只在我把它放在EndUpdate之后才起作用,但它不会在没有try-catch的情况下抛出任何错误?它现在已经修复了,还是应该放try-catch?FWIW,我在WinForms属性列表中没有看到TopItemIndex:它不会与LargeIcon一起工作,抛出:“无法在LargeIcon、SmallIcon或Tile视图中获取顶部项目”。多好啊。。
    listView1.BeginUpdate();
    listView1.Items.Clear();
    
    // loop through your add routine
    listView1.Items.Add(lvi);
    
    listView1.EndUpdate();