2009-09-16 8 views
3

Est-il possible de retirer la totalité de la section d'en-tête d'une liste? (y compris la région à droite des en-têtes de colonne)? ListView est en mode Détails.Dessiner en dehors de la zone de la colonne dans l'en-tête de la colonne listview

Une réponse indique ici que l'espace restant peut être établi avec le dernier en-tête de colonne: http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic32927.aspx

Mais il ne semble pas fonctionner du tout - rien est tiré hors de la zone d'en-tête.

La solution proposée est basée sur le dessin en dehors des limites passées:

if (e.ColumnIndex == 3) //last column index 
{ 
    Rectangle rc = new Rectangle(e.Bounds.Right, //Right instead of Left - offsets the rectangle 
      e.Bounds.Top, 
      e.Bounds.Width, 
      e.Bounds.Height); 

    e.Graphics.FillRectangle(Brushes.Red, rc); 
} 

La propriété ClipBounds de l'instance graphique disponible indique une zone non liée (un grand nombre de négatifs à grand positif). Mais rien n'est dessiné en dehors de la zone columnheader de la dernière colonne.

Quelqu'un at-il une solution pour cela?

Répondre

5

Je suis surpris par la réponse de Jeffery Tan dans cet article. Sa solution ne peut pas fonctionner, car le code essaie de dessiner en dehors de la zone du client de contrôle d'en-tête. Le hDC utilisé dans le dessin personnalisé (et donc le dessin du propriétaire) est pour la zone client du contrôle et ne peut donc pas être utilisé pour peindre dans la zone non-client. La zone située à droite de la colonne la plus à droite dans un contrôle d'en-tête se trouve dans une zone non-client. Vous avez donc besoin d'une solution différente.

Solutions possibles

  1. Salut technologie et partiellement efficace

Vous pouvez activer le dessin en dehors de la zone client en utilisant l'appel GetDC() WinAPI:

[System.Runtime.InteropServices.DllImport("user32")] 
private static extern IntPtr GetDC(IntPtr hwnd); 
[System.Runtime.InteropServices.DllImport("user32")] 
private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); 

public static IntPtr GetHeaderControl(ListView list) { 
    const int LVM_GETHEADER = 0x1000 + 31; 
    return SendMessage(list.Handle, LVM_GETHEADER, 0, 0); 
} 

En votre colonne dessine l'événement han dler, vous aurez besoin de quelque chose comme ceci:

if (e.ColumnIndex == 3) //last column index 
{ 
    ListView lv = e.Header.ListView; 
    IntPtr headerControl = NativeMethods.GetHeaderControl(lv); 
    IntPtr hdc = GetDC(headerControl); 
    Graphics g = Graphics.FromHdc(hdc); 

    // Do your extra drawing here 
    Rectangle rc = new Rectangle(e.Bounds.Right, //Right instead of Left - offsets the rectangle 
      e.Bounds.Top, 
      e.Bounds.Width, 
      e.Bounds.Height); 

    e.Graphics.FillRectangle(Brushes.Red, rc); 

    g.Dispose(); 
    ReleaseDC(headerControl, hdc); 
} 

Mais le problème est que depuis votre dessin est en dehors de la zone client, Windows ne sait pas toujours quand il doit être établi. Ainsi, il disparaît parfois, puis redessiné lorsque Windows pense que l'en-tête doit être repeint.

  1. basse technologie, mais laid

Ajouter une colonne vide supplémentaire à votre contrôle, propriétaire dessiner ne regarde comme vous le voulez, il est très large, et tourner à l'horizontale défilement (facultatif).

je sais que c'est horrible, mais vous cherchez des suggestions :)

  1. plus efficace, mais pas encore parfait

Utilisez ObjectListView. Cette enveloppe autour d'un .NET ListView vous permet d'ajouter des superpositions à votre liste - une superposition peut dessiner n'importe où dans le ListView, y compris l'en-tête.[Déclaration: Je suis l'auteur de ObjectListView, mais je pense toujours qu'il est la meilleure solution]

public class HeaderOverlay : AbstractOverlay 
{ 
    public override void Draw(ObjectListView olv, Graphics g, Rectangle r) { 
     if (olv.View != System.Windows.Forms.View.Details) 
      return; 

     Point sides = NativeMethods.GetColumnSides(olv, olv.Columns.Count-1); 
     if (sides.X == -1) 
      return; 

     RectangleF headerBounds = new RectangleF(sides.Y, 0, r.Right - sides.Y, 20); 
     g.FillRectangle(Brushes.Red, headerBounds); 
     StringFormat sf = new StringFormat(); 
     sf.Alignment = StringAlignment.Center; 
     sf.LineAlignment = StringAlignment.Center; 
     g.DrawString("In non-client area!", new Font("Tahoma", 9), Brushes.Black, headerBounds, sf); 
    } 
} 

Cela donne ceci: alt text http://i26.tinypic.com/2m7ex.jpg

[lecture sur cette réponse, je pense que ceci est un exemple de essayer trop dur :) J'espère que vous trouverez quelque chose ici utile.]

+0

Merci pour la bonne réponse. Dans notre cas, ObjectListView serait une overkill. Votre première solution est suffisante et fonctionne assez bien dans notre scénario. Vous avez une petite erreur dans le code - il faut naturellement dessiner sur l'instance g, pas sur l'original e.Graphics: e.Graphics.FillRectangle (Brushes.Red, rc); // doit être g.FillRectangle L'importation de méthode SendMessage est également manquante: [DllImport ("user32.dll", CharSet = CharSet.Auto)] public statique extern IntPtr SendMessage (IntPtr gérer, int messg, int wparam, int lparam); – Marek

+0

Oui, vous avez raison – Grammarian

+1

OLV est génial, mais je crois que vous devez gérer WM_PAINT et WM_ERASEBKGND dans HeaderControl pour être en mesure de dessiner entièrement l'en-tête personnalisé. Quelque chose comme ça: http://www.codeproject.com/Articles/4243/Customizing-the-header-control-in-a-ListView – thims

Questions connexes