是否存在允许用户在Windows窗体容器中滚动数百或数千个项目的常见设计?示例:编写电子邮件客户端,用户会得到平滑滚动超过10,000条消息“行”的印象,每封电子邮件一行 - 但这些肯定在它们显示之前才真正呈现。类似地,滚动一个巨大的图像必须要求将它拼凑成更小的部分,但代码是如何组织的呢?
答案 0 :(得分:4)
它是Windows设计方式的先天,不需要3个字母或特殊模式。
GUI程序,无论其风格如何,只要预期执行操作,就会从Windows获取消息。消息不按生成顺序处理。有三个基本的“优先事项”:
按上述顺序处理消息。首先发送消息,如果没有待处理消息,则程序开始清空消息队列。如果它为空,则调度合成消息。绘画属于第3类。当 nothing else需要完成时,程序才会收到WM_PAINT消息。
因此,基本的事件链是控件从消息队列中检索鼠标消息并检测到它是否适用于滚动条。它计算滚动条拇指的新位置,并调用InvalidateRect()winapi函数以指示需要绘制窗口。与Winforms中的Invalidate()方法功能相同。更新内部窗口状态以标记需要绘制以重新绘制该矩形。这一切都非常快,没有真正的绘画。
当程序检索下一条消息时,现在可以发生两件基本事情。当用户继续滚动时,它可能再次是鼠标消息。以与上面完全相同的方式处理,除了改变拇指位置之外,窗口没有任何反应。
或者没有要处理的新消息,用户停止滚动,现在类别#3合成消息转向。 Windows注意到窗口需要重新绘制并传递WM_PAINT消息。
除此之外还有一些实现细节,Windows的更高版本默认情况下启用“拖动时显示窗口内容”系统选项。这使得用户在拖动拇指时更容易看到他正在做什么,它故意生成额外的窗口涂料。总而言之,操作系统和程序可以很好地支持ListView中的数万个项目。当然不是用户。
答案 1 :(得分:2)
如前所述,VirtualMode可能是最佳解决方案,有一个usage example on CodeProject。
在表单创建时初始化虚拟模式:
private void Form1_Load(object sender, EventArgs e)
{
listView1.VirtualMode = true; // switching virtual mode on
listView1.VirtualListSize = 1000000000; // give it 1 million lines
}
然后分配并处理RetrieveVirtualItem
事件:
private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
ListViewItem lvi = new ListViewItem(); // create a listviewitem object
lvi.Text = nt.MakeText(e.ItemIndex); // assign the text to the item
ListViewItem.ListViewSubItem lvsi = new ListViewItem.ListViewSubItem(); // subitem
NumberFormatInfo nfi = new CultureInfo("de-DE").NumberFormat;
nfi.NumberDecimalDigits = 0;
lvsi.Text = e.ItemIndex.ToString("n", nfi); // the subitem text
lvi.SubItems.Add(lvsi); // assign subitem to item
e.Item = lvi; // assign item to event argument's item-property
}