首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在带有ownerdraw和virtualmode的列表视图中闪烁

在带有ownerdraw和virtualmode的列表视图中闪烁
EN

Stack Overflow用户
提问于 2009-06-02 10:48:37
回答 3查看 5.8K关注 0票数 2

我正在使用带有以下参数设置的listview控件:

代码语言:javascript
复制
        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);

向两行提供一些随机文本。Ownerdrawing很简单:

代码语言:javascript
复制
    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);
    }

问题是:当我将鼠标悬停在listview的内容上时,我得到了第一列的闪烁。调试显示,当鼠标悬停在DrawSubItem上时,会不断地调用它。

是bug吗?如何避免这种行为?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-06-02 15:27:06

这是.NET的ListView中的一个错误,您不能通过双缓冲来绕过它。

在虚拟列表中,当鼠标悬停在列0上时,底层控件会生成大量自定义绘制事件。即使启用了DoubleBuffering,这些自定义绘制事件也会导致闪烁,因为它们是在正常的WmPaint消息之外发送的。

我似乎还记得这只发生在XP上。Vista修复了这个问题(但也引入了其他问题)。

您可以查看ObjectListView中的代码,了解它是如何解决这个问题的。

如果你想自己解决这个问题,你需要深入研究ListView控件的内部机制:

  1. override WndProc
  2. intercept WmPaint msg,并在msg
  3. 截取WmCustomDraw msg期间设置为true的标志,并忽略发生在WmPaint事件之外的所有msg。

如下所示::

代码语言:javascript
复制
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;
    }
}
票数 5
EN

Stack Overflow用户

发布于 2009-11-05 19:52:56

我收到了一堆

代码语言:javascript
复制
'System.Drawing.NativeMethods' is inaccessible due to its protection level

代码语言:javascript
复制
The type name 'NMHDR' does not exist in the type 'System.Drawing.NativeMethods' 

错误。我在某处读到我必须包含user32.dll,但不知道在这种情况下该怎么做。

编辑:好的,我甚至在开始思考之前就发布了。现在,我创建了自己的ListView控件,并复制了objectListView代码中的结构。它现在似乎起作用了。下面是我的代码:

代码语言:javascript
复制
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;
        }
    }

}
票数 4
EN

Stack Overflow用户

发布于 2009-06-02 15:01:04

我不能为ListView频繁调用自定义绘制事件提供解决方案,但也许你可以用双缓冲来掩盖这个问题:

Stackoverflow: How to double buffer .NET controls on a form?

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/938896

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档