2009-06-02 6 views
2

J'utilise le contrôle ListView avec les paramètres suivants sont définis:vacillant dans listview avec OwnerDraw et 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); 

Deux lignes sont fournis avec un texte aléatoire. Ownerdrawing est 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); 
    } 

le problème est le suivant: quand je souris sur planer le contenu de listview, je reçois vacillante de la première colonne. Le débogage montre que DrawSubItem est appelé en permanence lorsque la souris le survole.

Est-ce bug? Comment éviter ce comportement?

+0

Ceci est une vieille question, mais la réponse accceptée n'est pas correcte, ou du moins pas à partir de .NET 4.0. Vérifiez l'attribut protégé DoubleBuffered de la classe ListView, et éventuellement ma réponse à [this] (http: // stackoverflow.com/questions/10484265/question flickering-in-listview-control-ownerdraw-virtual/10501938 # 10501938). – zmilojko

+0

La réponse donnée est complètement correcte. Sur XP, si vous avez une liste virtuelle et survolez la colonne 0, le contrôle clignotera. DoubleBuffered = true ne fait aucune différence. Il est vrai que sur Windows 7, ce problème ne se produit pas, mais cela ne rend pas cette réponse incorrecte. – Grammarian

Répondre

5

Ce bogue dans .NET de ListView et vous ne pouvez pas le contourner en double buffering. Sur les listes virtuelles, le contrôle sous-jacent génère de nombreux événements de dessin personnalisés lorsque la souris survole la colonne 0. Ces événements de dessin personnalisés provoquent des scintillements même si vous activez DoubleBuffering parce qu'ils sont envoyés en dehors du message WmPaint normal.

J'ai aussi l'impression de me souvenir que cela n'arrive que sous XP. Vista a corrigé celui-ci (mais en a introduit d'autres).

Vous pouvez regarder le code dans ObjectListView pour voir comment il a résolu ce problème.

Si vous voulez résoudre vous-même, vous avez besoin de se plonger dans la plomberie intérieure du contrôle ListView:

  1. override WndProc
  2. intercepter le WMPaint msg, et définir un indicateur qui est vrai au cours de la msg
  3. interceptez le message WmCustomDraw et ignorez tous les messages qui se produisent en dehors d'un événement WmPaint.

Quelque chose comme ceci ::

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

brillant, merci –

+0

Dans mon cas, j'ai annulé chaque fonction OnDrawXXX et préfacé chaque fonction avec une vérification de (this.isInWmPaintMsg == true). Comme cette méthode, il a résolu le problème pour moi (au moins sur Win7). J'ai supprimé le cas 0x204E, qui est une bonne solution, mais il repose sur la constante WM_REFLECT .NET interne définie à 0x2000. Bien que cela soit presque certain de ne jamais changer, WM_PAINT ne changera JAMAIS. Trouvé sur google, merci beaucoup pour les pointeurs! – Michael

+0

WM_REFLECT_NOTIFY n'est pas spécifique à .NET. Il a été utilisé depuis la programmation Windows de Petzold. MFC l'utilise intensivement. – Grammarian

3

je reçois un tas de

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

et

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

erreurs. J'ai lu quelque part que je dois inclure user32.dll mais ne peux pas comprendre comment le faire dans ce cas.

Modifier: OK, j'ai posté avant même de commencer à penser. J'ai créé maintenant mon propre contrôle ListView et copié la structure à partir du code objectListView. Ça semble fonctionner maintenant. Voici mon code:

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

} 
Questions connexes