为什么虚拟ListView的不可见项没有索引? [英] Why the Items of a Virtual ListView that are not visible don't have index?

查看:112
本文介绍了为什么虚拟ListView的不可见项没有索引?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在以虚拟模式(.NET 4.6)使用ListView.
我试图在虚拟ListView中找到项的索引:输入字母时,应该选择带有以该字母开头的文本的第一个项目.

I'm working with a ListView in Virtual Mode (.NET 4.6).
I tried to find the index of Items in the virtual ListView: when I enter a letter, the first item with text that start with that letter should be selected.

这是listView1_KeyDown中的FindItemWithText:

if (char.IsLetterOrDigit(e.KeyChar))
{
    var str = e.KeyChar.ToString();
    if (tempStr != str)
    {
        Index = 0;
        tempStr = str;
    }

    var item = listView1.FindItemWithText(str, false, Index, true);
    if (item != null)
    {
        item.Selected = true;
        item.Focused = true;
        item.EnsureVisible();
        Index = item.Index + 1;
    }
}

这是我的SearchForVirtualItem方法:

Here is my SearchForVirtualItem method:

var item = lvis.OfType<ListViewItem>().FirstOrDefault(
    i => i.Text.ToLower().StartsWith(e.Text.ToLower()) && 
         i.Index >= e.StartIndex);
if (item == null)
{

}
else
{
    e.Index = item.Index;
}

如果在滚动所有代码之前结果是可见项之一,则我可以选择结果项.但是,如果结果不可见,并且我根本没有滚动任何内容,则该方法返回null.

If the result is one of the visible items before I scroll at all the code works and I can select the result item. But if the result is not visible and I didn't scroll anything at all the method return null.

但是,即使我滚动到列表的末尾,即使有一次,我也能获得之前无法获得的项目索引.

But if I scroll to the end of the list even once I can get the index of the item that before I couldn't.

示例:如果我的虚拟列表中有200个项目(从列表中填充) 200 ListViewItem),并且只有前50个可见(如果我按c 字母和以c字母开头的项目在前50个字符中,它们将 被选中.
但是,如果我按x键,则虚拟ListView中的项目是 在最后一个50处,该方法将返回null.如果我改为将列表滚动到 最后,然后按xx开头的字符将被选中.

Example: If I have 200 items in a virtual list (populated from a list of 200 ListViewItem) and only the first 50 are visible, if I press the c letter and items that start with c letter are among the first 50, they will be selected.
But if I press x and the items in the virtual ListView are at the last 50, the method will return null. If I instead scroll the list to the end and then I press x, the items that start with x will be selected.

为什么我必须至少显示一次该项目才能拥有索引,而没有 index = -1 ?
这是虚拟ListView的正常行为还是有问题?

Why I have to show the item at least once to have an index and not having index = -1?
Is this the normal behavior of a virtual ListView or is there something wrong?

旁边的问题,在 normal 模式下的ListView何时变慢?在100,000个项目之后,还是在1,000,000个项目之后?

Side question, when does a ListView in normal mode become slow? After 100,000 items, or 1,000,000 items?


这是我的listView1_RetrieveVirtualItem代码:


Here is my listView1_RetrieveVirtualItem code:

private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    if (lvis.Count > 0)
    {
        e.Item = lvis[e.ItemIndex];
    }
}

我不使用缓存.
我使用BackGroundWorker从SQLite数据库获取数据.我创建ListViewitems并将其添加到列表(var lvis = new List<ListViewItem>).

I don't use a cache.
I use BackGroundWorker to get the data from a SQLite database; I create ListViewitems and add them to a List (var lvis = new List<ListViewItem>).

RunWorkerCompleted方法:

private void Pl_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    var obj = e.Result;
    if (obj != null)
    {
        RemoveSelection();

        lvis = (List<ListViewItem>)obj;
        listView1.Items.Clear();
        listView1.VirtualListSize = lvis.Count;
        listView1.Invalidate();

        var No_of_items = listView1.Items.Count + " pin(s)";
        count.Text = No_of_items;
        tabLabel.Text = GetButton().Text + " | " + No_of_items;
    }
}

lvis是虚拟ListView从其获取数据的源.

lvis is the source where the virtual ListView get its data from.

推荐答案

这似乎是与存储的ListViewItem Index值有关的简单误解:创建ListViewItem时,无法设置Index ,因此此方法可检索并返回匹配的ListViewItem:

It looks like it's a simple misunderstanding related to the stored ListViewItem Index value: when you create a ListViewItem, you cannot set the Index, so this method to retrieve and return a matching ListViewItem:

private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    var item = lvis.OfType<ListViewItem>().FirstOrDefault([...]); 
    e.Index = item.Index;
}

失败: item.Index 始终为-1,因为在创建ListViewItem时从未设置.
这就是ListView将查找已经显示的Items的原因(它们具有索引,虚拟列表不需要调用SearchForVirtualItem()来检索它们,它只需调用FindItem()).

will fail: item.Index is always -1, since was never set when the ListViewItem was created.
That's why the ListView will find Items that have already been shown (these have an Index, the virtual list doesn't need to retrieve them calling SearchForVirtualItem(), it will just call FindItem()).

一个简单的解决方案是使用e.Index 的值. rel ="nofollow noreferrer"> ListView.SearchForVirtualItem 处理程序是期望的.

A simple solution is to use the List.FindIndex() method, instead of finding an Item using FirstOrDefault(). This method returns the index in the List that contains the object that meets the criteria defined by the Predicate<T> argument.
This is the value of e.Index that the ListView.SearchForVirtualItem handler is expecting.

ListView在变得难以管理或变得太慢之前可以容纳多少个项目:如果没有任何进一步的规范,这是一个很难回答的问题.在列表模式下,对于100000项目,它可能表现得很好(如示例中所示),但是设置View = View.Details可能会完全改变方案.是否还必须处理图形对象?好吧,多大?在这种情况下,需要多少把柄?实际上,这是您在测试不同方案时回答自己的问题.
还应考虑用户"角度(还是应该首先考虑?:).也许列表可以轻松滚动,但是找到特定项目也容易吗?
如果您要在用户界面中显示很多项目,则很可能应该将它们组织在子类别中,并提供简单的快速可视方法来搜索和过滤它们,因此您的用户最终会使用子集少得多,可能更接近他们实际需要使用或找到的子集.

How many items a ListView can hold before it becomes difficult to manage or too slow: without any further specifications, this is a question difficult to answer. It may behave perfectly fine with 100000 items in List mode (as in the example), but setting the View = View.Details may change the scenario completely. Does it have to also handle graphics object? Well, how large? How many handles are needed, in this case? In practice, it's a question you answer yourself testing different scenarios.
The User perspective is also to consider (or should it come first? :). Maybe the list is scrollable with ease, but is it also easy to locate a specific Item?
If you have a lot of Items to present in the UI, you should most probably organize them in sub cathegories and provide easy, quick, visual methods to search and filter them, so your Users end up working with much less crowded subsets, probably closer to what they actually need to use or find.

这是一个修复程序和一个代码示例,应该可以测试

Here's a fix and a code sample that should allow to test the functionality of the ListView.FindItemWithText() method (this one also needs a small tweak).

  • ListView.VirtualMode 是在设计器中设置的
  • 在此示例中,ListViewItems集合由1,000个项目的列表表示,重复了100次,因此ListView VirtualListSize被设置为100,000个项目
  • The ListView.VirtualMode is set in the Designer
  • In the example, the ListViewItems collection is represented by a list of 1,000 items, repeated 100 times, so the ListView VirtualListSize is set to 100,000 items

btnLVSearch :用于搜索ListView项的按钮.
btnLVLoadData :用于加载数据并设置VirtualListSize的按钮.
chkPrefixSearch :选择PrefixSearchTextSearch的复选框.
chkCaseSensitiveSearch :用于设置/重置区分大小写的搜索的复选框

btnLVSearch: the Button used to search the ListView items.
btnLVLoadData: the Button used to load the data and sets the VirtualListSize.
chkPrefixSearch: the CheckBox that selects a PrefixSearch or a TextSearch.
chkCaseSensitiveSearch: the CheckBox used to set/reset the case sensitive search

int currentStartIndex = 0;
List<ListViewItem> listItems = null;

private void btnLVLoadData_Click(object sender, EventArgs e)
{
    listItems = new List<ListViewItem>();
    // [...]
    //  Fill the listItems collection  
    listView1.VirtualListSize = listItems.Count;
}

private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    if (e.ItemIndex >= 0) {
        e.Item = listItems[e.ItemIndex];
    }
}

private void listView1_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
{
    StringComparison comparison = chkCaseSensitiveSearch.Checked 
                                ? StringComparison.CurrentCulture 
                                : StringComparison.CurrentCultureIgnoreCase;
    int itemIndex = -1;
    if (e.IsPrefixSearch) {
        itemIndex = listItems.FindIndex(e.StartIndex, 
            itm => itm.Text.StartsWith(e.Text, comparison));
    }
    else if (e.IsTextSearch) {
        itemIndex = listItems.FindIndex(e.StartIndex, 
            itm => itm.Text.IndexOf(e.Text, comparison) >= 0);
    }
    e.Index = itemIndex;
}

private void btnLVSearch_Click(object sender, EventArgs e)
{
    var item = listView1.FindItemWithText(
        txtLVSearch.Text, false, currentStartIndex, chkPrefixSearch.Checked);

    if (item != null) {
        currentStartIndex = item.Index + 1;
        listView1.SelectedIndices.Add(item.Index);
        item.Selected = true;
        listView1.EnsureVisible(item.Index);
        listView1.Focus();
    }
    else {
        currentStartIndex = 0;
    }
}

在处理ListView.KeyPress事件时,将e.Handled = true设置为抑制按键,否则在分配e.Index = itemIndex后立即触发第二个SearchForVirtualItem事件(这次,将e.IsPrefixSearch设置为false ):

When handling the ListView.KeyPress event, set e.Handled = true to suppress the key press, otherwise a second SearchForVirtualItem event is triggered immediately after e.Index = itemIndex is assigned (this time, with e.IsPrefixSearch set to false):

private void listView1_KeyPress(object sender, KeyPressEventArgs e)
{
    e.Handled = true;
    var item = listView1.FindItemWithText(
        e.KeyChar.ToString(), false, currentStartIndex, chkPrefixSearch.Checked);
    // [...]
}

这篇关于为什么虚拟ListView的不可见项没有索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆