在 .NET 和 XP 中实现无闪烁的 ListView






4.88/5 (24投票s)
2003年1月27日
6分钟阅读

360928

4520
本文讨论了 .NET ListView 的问题并提供了修复这些问题的解决方案。
引言
本文的重点是扩展和改进 .NET ListView
控件。当前的 ListView
控件非常有用,但存在一个恼人的闪烁问题。我的目标是消除或显著减少这种闪烁。本文提供的解决方案仅适用于带有关联清单文件的 Windows XP。这是我为改进 ListView
控件编写的三篇文章中的第一篇。其他文章不需要 XP。此外,我在本文中讨论的其他内容也不需要 XP。
它会闪烁
您会遇到闪烁的两种情况。
动态添加项目
如果您尝试在使用具有多个图标的
ImageList
时添加多个项目(在for
或foreach
循环中),并且希望这些项目在添加时显示,您需要使用Refresh()
或Update()
(您可以使用其他方法,如Application.DoEvents()
)。上述任何一种方法都会导致恼人的闪烁。要避免闪烁,请不要将Refresh()
、Update()
等放在for
循环内。这将导致ListView
在所有项目添加完成之前保持空白。有时,这是最佳方法,但如果这样做需要大量时间,则不是一个好主意,因为用户会认为程序已锁定。调整带有停靠的 ListView 的主窗体大小
当
ListView
停靠时,如同在 Windows 资源管理器中一样(并且您有大量项目),调整主窗体大小时会导致所有项目闪烁。
更新:我最初有三个。第三个是更新/更改项目,但您只需使用 Update()
而不是 Refresh()
即可解决此问题。
为什么会闪烁?
当您向 ListView
添加新项目时,控件会自行失效。这会导致背景被擦除,所有项目都会重新绘制。它应该只使新项目的边界失效,只绘制项目,但事实并非如此。这意味着调用 Refresh()
和 Update()
会产生相同的结果,而您期望 Update()
只更新新项目或更改的项目。在我的下一篇文章中,我将讨论几种技术,允许只重绘新添加的项目。
它闪烁的另一个原因是它不是真正双缓冲的。您可以扩展 ListView
类,并添加 SetStyle(ControlStyles.DoubleBuffer, true)
,但这不起作用。此处描述的技术向您展示了如何对 ListView
进行双缓冲。
ListView 的双缓冲
如果您熟悉仅使用 Win32 API 或 MFC 编程 ListView
(或 CListCtrl
),您会注意到您可以为 ListView
设置特定样式。其中大多数样式已在 .NET ListView
中设置,并且是可自定义的(例如,ViewModes
,图标如何对齐...是否自动排列等)。
此外,还有 ListView
的“扩展”样式。这些样式添加了一些额外功能,例如使用单击激活和热跟踪(类似于 Windows 98 资源管理器以 Web 模式浏览,单击项目一次即可启动该项目,“进入”该项目时会选中该项目等)。还有一个扩展样式允许在详细信息/报告模式下围绕项目绘制网格线(这在 .NET 中已实现,当您选择详细信息视图并显示网格线时)。
在 Windows XP 中,有一个名为 LVS_EX_DOUBLEBUFFER
的扩展样式。这就是我们想要的。不幸的是,它仅适用于通用控件版本 6,这意味着我们需要 Windows XP 以及清单文件(或嵌入式资源)。此外,还有一个名为 LVS_EX_BORDERSELECT
的样式,它告诉 ListView
(在大图标模式下)突出显示图标的边框,而不是覆盖它(这在通用控件版本 4.71 中可用,因此无需 XP 即可工作)。
因此,我们的目标是实际利用这些扩展样式。如果您熟悉 SendMessage
Windows 函数,这会很容易。我们从 user32.dll 导入此函数,并使用它来检索和设置扩展样式。代码如下:
public void SetExStyles()
{
LVS_EX styles = (LVS_EX)SendMessage(this.Handle,
(int) LVM.LVM_GETEXTENDEDLISTVIEWSTYLE, 0,0);
styles |= LVS_EX.LVS_EX_DOUBLEBUFFER | LVS_EX.LVS_EX_BORDERSELECT;
SendMessage(this.Handle,
(int) LVM.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (int) styles);
}
我们首先检索任何现有的扩展样式。然后添加 DOUBLEBUFFER
和 BORDERSELECT
,并使用 SendMessage
将这些额外样式添加到当前 ListView
。此方法应在 ListView
的句柄创建之后调用。否则,它将不起作用,因为 Handle
尚未定义。换句话说,不要在主窗体的构造函数中调用此方法;它应该在整个窗体加载完成后发生。
注意:LVS_EX
是 int 的枚举类型。其中的每个常量都定义了正确的 ExtendedStyle
。LVM
也是代表 ListView
消息的枚举类型。
一旦调用了此方法,您就可以使用类似这样的循环将项目添加到此 ListView
中:
for (int i=0; i < 500; i++)
{
listviewXp.Items.Add("item name", iconIndex);
listviewXp.Update();
}
现在您将能够看到正在绘制的项目,并且不再遇到恼人的闪烁。此外,如果您的 ListView
已停靠,您可以调整窗体大小而不会出现闪烁问题。您也可以在对现有项目进行更改后调用 listview.Refresh()
或 listview.Update()
,并且更改将无闪烁地完成。
运行和使用示例
使用演示示例
请确保您使用的是 Windows XP,并将 exe 和清单文件放在同一个目录中。运行它,然后按按钮以观看项目无闪烁地出现。如果您没有使用 XP,您仍然会看到闪烁,但您将能够看到
BorderSelect
模式。编译示例应用程序
打开 Visual Studio .NET 并进行编译。在运行之前,将清单文件复制到 exe 所在的 Debug 或 Release 目录。
在您自己的应用程序中使用 ListViewXP
只需复制 ListViewXP.cs 文件并将其添加到您的应用程序中。请注意,如果您想要双缓冲效果,必须提供清单文件。您可以复制我提供的文件,并将其重命名为 YourAppName.exe.manifest。您可以自由使用和分发此类,但如果您这样做,请包含一个注释,其中包含我的姓名和本文的链接。另外,如果您使用它,请告诉我它的效果如何。
结论
使用来自 user32.dll 的 SendMessage
函数,我们可以扩展 .NET ListView
,以便在 XP 中添加双缓冲支持以消除闪烁。此外,我们可以使用此函数添加其他扩展的 ListView
样式,并且此技术也可以应用于其他控件。希望您喜欢这篇文章。