I'm using listview control with the following parameters set:
我正在使用listview控件并设置以下参数:
this.listView1.BackColor = System.Drawing.Color.Gainsboro;
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1,
this.columnHeader2});
this.listView1.FullRowSelect = true;
this.listView1.HideSelection = false;
this.listView1.Location = new System.Drawing.Point(67, 192);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(438, 236);
this.listView1.TabIndex = 0;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
this.listView1.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.listView1_DrawColumnHeader);
this.listView1.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.listView1_RetrieveVirtualItem);
this.listView1.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.listView1_DrawSubItem);
Two rows are provided with some random text. Ownerdrawing is simple:
两行提供一些随机文本。拥有者绘图很简单:
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
if (e.ColumnIndex == 0)
{
e.DrawBackground();
e.DrawText();
}
else
e.DrawDefault = true;
//Console.WriteLine("{0}\t\tBounds:{1}\tItem:{2}\tSubitem:{3}", (i++).ToString(), e.Bounds.ToString(), e.Item, e.SubItem);
}
the problem is: when i hover mouse on listview's content, i get flickering of first column. Debugging shows that DrawSubItem is called constantly while the mouse is over it.
问题是:当我将鼠标悬停在listview的内容上时,我会看到第一列的闪烁。调试显示DrawSubItem在鼠标悬停时不断调用。
Is it bug? How to avoid this behavour?
是bug吗?如何避免这种行为?
3 个解决方案
#1
This is a bug in .NET's ListView and you cannot get around it by double buffering.
这是.NET的ListView中的一个错误,你不能通过双缓冲绕过它。
On virtual lists, the underlying control generates lots of custom draw events when the mouse is hover over column 0. These custom draw events cause flickering even if you enable DoubleBuffering because they are sent outside of the normal WmPaint msg.
在虚拟列表上,当鼠标悬停在第0列上时,基础控件会生成大量自定义绘制事件。即使启用DoubleBuffering,这些自定义绘制事件也会导致闪烁,因为它们是在正常的WmPaint消息之外发送的。
I also seem to remember that this only happens on XP. Vista fixed this one (but introduced others).
我似乎也记得这只发生在XP上。 Vista修复了这个(但引入了其他人)。
You can look in at the code in ObjectListView to see how it solved this problem.
您可以查看ObjectListView中的代码,看看它是如何解决这个问题的。
If you want to solve it yourself, you need to delve into the inner plumbing of the ListView control:
如果你想自己解决它,你需要深入研究ListView控件的内部管道:
- override WndProc
- intercept the WmPaint msg, and set a flag that is true during the msg
- intercept the WmCustomDraw msg, and ignore all msgs that occur outside of a WmPaint event.
拦截WmPaint消息,并设置一个在msg期间为真的标志
拦截WmCustomDraw消息,并忽略WmPaint事件之外发生的所有消息。
Something like this::
像这样的东西::
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x0F: // WM_PAINT
this.isInWmPaintMsg = true;
base.WndProc(ref m);
this.isInWmPaintMsg = false;
break;
case 0x204E: // WM_REFLECT_NOTIFY
NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR));
if (nmhdr.code == -12) { // NM_CUSTOMDRAW
if (this.isInWmPaintMsg)
base.WndProc(ref m);
} else
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
#2
I get a bunch of
我得到了一堆
'System.Drawing.NativeMethods' is inaccessible due to its protection level
and
The type name 'NMHDR' does not exist in the type 'System.Drawing.NativeMethods'
errors. I read somewhere that I have to include user32.dll but can't figure out how to do it in this case.
错误。我在某处读到了我必须包含user32.dll但在这种情况下无法弄清楚如何做到这一点。
Edit: OK, I posted before even start to think. I created now my own ListView control and copied the struct from the objectListView code. It seems to work now. Here my code:
编辑:好的,我发布之前甚至开始思考。我现在创建了自己的ListView控件,并从objectListView代码中复制了struct。它现在似乎工作。这是我的代码:
public class Listview : ListView
{
private bool isInWmPaintMsg=false;
[StructLayout(LayoutKind.Sequential)]
public struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0F: // WM_PAINT
this.isInWmPaintMsg = true;
base.WndProc(ref m);
this.isInWmPaintMsg = false;
break;
case 0x204E: // WM_REFLECT_NOTIFY
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == -12)
{ // NM_CUSTOMDRAW
if (this.isInWmPaintMsg)
base.WndProc(ref m);
}
else
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
}
#3
i can't offer a solution to the ListView calling custom draw events too often, but perhaps you can just mask the problem with double-buffering:
我不能经常为ListView调用自定义绘制事件提供解决方案,但也许您可以通过双缓冲来掩盖问题:
*: How to double buffer .NET controls on a form?
*:如何在表单上双重缓冲.NET控件?
#1
This is a bug in .NET's ListView and you cannot get around it by double buffering.
这是.NET的ListView中的一个错误,你不能通过双缓冲绕过它。
On virtual lists, the underlying control generates lots of custom draw events when the mouse is hover over column 0. These custom draw events cause flickering even if you enable DoubleBuffering because they are sent outside of the normal WmPaint msg.
在虚拟列表上,当鼠标悬停在第0列上时,基础控件会生成大量自定义绘制事件。即使启用DoubleBuffering,这些自定义绘制事件也会导致闪烁,因为它们是在正常的WmPaint消息之外发送的。
I also seem to remember that this only happens on XP. Vista fixed this one (but introduced others).
我似乎也记得这只发生在XP上。 Vista修复了这个(但引入了其他人)。
You can look in at the code in ObjectListView to see how it solved this problem.
您可以查看ObjectListView中的代码,看看它是如何解决这个问题的。
If you want to solve it yourself, you need to delve into the inner plumbing of the ListView control:
如果你想自己解决它,你需要深入研究ListView控件的内部管道:
- override WndProc
- intercept the WmPaint msg, and set a flag that is true during the msg
- intercept the WmCustomDraw msg, and ignore all msgs that occur outside of a WmPaint event.
拦截WmPaint消息,并设置一个在msg期间为真的标志
拦截WmCustomDraw消息,并忽略WmPaint事件之外发生的所有消息。
Something like this::
像这样的东西::
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x0F: // WM_PAINT
this.isInWmPaintMsg = true;
base.WndProc(ref m);
this.isInWmPaintMsg = false;
break;
case 0x204E: // WM_REFLECT_NOTIFY
NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR));
if (nmhdr.code == -12) { // NM_CUSTOMDRAW
if (this.isInWmPaintMsg)
base.WndProc(ref m);
} else
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
#2
I get a bunch of
我得到了一堆
'System.Drawing.NativeMethods' is inaccessible due to its protection level
and
The type name 'NMHDR' does not exist in the type 'System.Drawing.NativeMethods'
errors. I read somewhere that I have to include user32.dll but can't figure out how to do it in this case.
错误。我在某处读到了我必须包含user32.dll但在这种情况下无法弄清楚如何做到这一点。
Edit: OK, I posted before even start to think. I created now my own ListView control and copied the struct from the objectListView code. It seems to work now. Here my code:
编辑:好的,我发布之前甚至开始思考。我现在创建了自己的ListView控件,并从objectListView代码中复制了struct。它现在似乎工作。这是我的代码:
public class Listview : ListView
{
private bool isInWmPaintMsg=false;
[StructLayout(LayoutKind.Sequential)]
public struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x0F: // WM_PAINT
this.isInWmPaintMsg = true;
base.WndProc(ref m);
this.isInWmPaintMsg = false;
break;
case 0x204E: // WM_REFLECT_NOTIFY
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == -12)
{ // NM_CUSTOMDRAW
if (this.isInWmPaintMsg)
base.WndProc(ref m);
}
else
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
}
#3
i can't offer a solution to the ListView calling custom draw events too often, but perhaps you can just mask the problem with double-buffering:
我不能经常为ListView调用自定义绘制事件提供解决方案,但也许您可以通过双缓冲来掩盖问题:
*: How to double buffer .NET controls on a form?
*:如何在表单上双重缓冲.NET控件?