2009-06-02 51 views
2

我使用的是設置以下參數ListView控件:提供了一些隨機文本在列表視圖與閃爍和的OwnerDraw virtualmode

 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); 

兩行。自主提款很簡單:

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在鼠標懸停時被不斷調用。

這是錯誤嗎?如何避免這種行爲?

+0

這是一個老問題,但被接受的答案是不正確的,或者至少不是.NET 4.0的。檢查ListView類的DoubleBuffered受保護屬性,並可能回答[this](http:// stackoverflow。com/questions/10484265/flickering-in-listview-control-ownerdraw-virtual/10501938#10501938)問題。 – zmilojko 2012-05-08 15:46:48

+0

給出的答案是完全正確的。在XP上,如果您有虛擬列表並將鼠標懸停在第0列上,則控件將閃爍。 DoubleBuffered = true沒有區別。誠然,在Windows 7上,這個問題不會發生,但這並不會使這個答案不正確。 – Grammarian 2012-05-11 05:00:41

回答

5

這是在.NET中的一個bug ListView,你不能通過雙緩衝來解決它。

在虛擬列表上,當鼠標懸停在列0上時,底層控件會生成大量自定義繪製事件。即使您啓用DoubleBuffering,這些自定義繪製事件也會導致閃爍,因爲它們是在正常WmPaint信息之外發送的。

我似乎也記得這隻發生在XP上。 Vista修復了這一個(但介紹了其他)。

您可以查看ObjectListView中的代碼,看看它是如何解決這個問題的。

如果你想自己解決問題,你需要深入研究的ListView控件內管道:

  1. 覆蓋的WndProc
  2. 攔截WmPaint味精,並設置一個標誌,在是真實的msg
  3. 攔截WmCustomDraw消息,並忽略發生在WmPaint事件之外的所有消息。

像這樣的東西::

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; 
    } 
} 
3

我收到了一堆

'System.Drawing.NativeMethods' is inaccessible due to its protection level 

The type name 'NMHDR' does not exist in the type 'System.Drawing.NativeMethods' 

錯誤。我讀過的地方,我必須包括user32.dll,但無法弄清楚在這種情況下如何做到這一點。

編輯︰好吧,我發佈之前,甚至開始思考。我現在創建了我自己的ListView控件,並從objectListView代碼複製了結構。它現在似乎工作。這裏我的代碼:

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; 
     } 
    } 

}