2012-01-30 16 views
8

Tengo un ListView donde deseo ajustar el dibujo de los elementos (por ejemplo, resaltar ciertas cadenas en los elementos de vista de lista), sin embargo, no quiero alterar radicalmente la forma en que se muestran los elementos.Implementación predeterminada para ListView OwnerDraw

He configurado el OwnerDraw como verdadero y puedo entender cómo dibujar mi efecto de resaltado, sin embargo cada vez que trato de diferir a la implementación predeterminada para dibujar el resto de la lista veo que las cosas van mal y estoy me quedé con una gran cantidad de problemas gráficos que indicaban que realmente me había equivocado completamente.

¿Hay algún lugar donde pueda ver lo que los controladores "Predeterminados" para los eventos DrawItem y DrawSubItem hacen para que yo pueda entender mejor y modificar más fácilmente mi código?

Como referencia Aquí hay un fragmento que muestra lo que estoy haciendo actualmente:

public MyListView() 
{ 
    this.OwnerDraw = true; 
    this.DoubleBuffered = true; 
    this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader); 
    this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem); 
    this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem); 
} 

private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
{ 
    // Not interested in changing the way columns are drawn - this works fine 
    e.DrawDefault = true; 
} 

private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e) 
{ 
    e.DrawBackground(); 
    e.DrawFocusRectangle(); 
} 

private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) 
{ 
    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 
    if (index >= 0) 
    { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
    } 

    e.DrawText(); 
} 
+0

Creo que es necesario agregar más información acerca de lo que va mal. Probé tu código y fue ... está bien. Se destacó la palabra "Término" con un cuadro amarillo. Tal vez muestre una imagen de los resultados del dibujo. – LarsTech

+0

@LarsTech Recibo una amplia gama de problemas, que van desde que el efecto de selección ya no es visible (que se puede ver en la muestra anterior) hasta que el texto del elemento se "renderiza doblemente" y también desaparece cuando coloco el mouse sobre el elemento. No espero que alguien pueda solucionar mágicamente ninguno/todos estos problemas (que también sería demasiado específico para ayudar a los futuros usuarios), sino que me gustaría ver una implementación de ejemplo que haga que todo sea lo más parecido posible a la implementación estándar – Justin

Respuesta

10

No tengo tiempo ahora para escribir una respuesta completa, así que en su lugar pondré algunas notas rápidas y volveré más tarde.

Como dijo LarsTech, dueño del dibujo un control ListView es un dolor - la clase .Net ListView es una envoltura alrededor de la Win32 List View Control subyacente y la capacidad de "dibujar Propietario" es proporcionada por el NM_CUSTOMDRAW notification code. Como tal, no existe una "implementación .Net predeterminada": el valor predeterminado es usar el control Win32 subyacente.

para hacer la vida aún más difícil que hay una serie de consideraciones adicionales para hacer:

  • Como LarsTech señaló, el primer subelemento, de hecho, representa el elemento principal en sí, y por lo que si usted maneja la representación en tanto DrawItem y DrawSubItem bien puede estar dibujando el contenido de la primera celda dos veces.
  • Hay un error en el control de vista de lista subyacente (documentado en la nota sobre this page) que significa que un evento DrawItem ocurrirá sin el correspondiente DrawSubItem eventos, lo que significa que si se dibuja un fondo en el caso DrawItem y luego dibujar el texto en el evento DrawSubItem, el texto de tu artículo desaparecerá cuando pases el mouse sobre él.
  • Parte del renderizado también parece no tener doble buffer por defecto
  • También noté que la propiedad ItemState no siempre es correcta, por ejemplo, justo después de cambiar el tamaño de una columna. En consecuencia, he encontrado que es mejor no confiar en eso.
  • También debe asegurarse de que su texto no se divida en varias líneas, de lo contrario verá los primeros píxeles de la línea inferior que se representan en la parte inferior de la celda.
  • También se debe prestar especial atención cuando se renderiza la primera celda para tener en cuenta el relleno extra que utiliza la vista de lista nativa.
  • Como el evento DrawItem se produce primero, todo lo que dibuje en el controlador DrawItem (por ejemplo, el efecto de selección) puede superponerse con cosas que hace en el controlador DrawSubItem (por ejemplo, tener ciertas celdas con un color de fondo diferente).

Con todo el manejo de dibujo propietario es un asunto bastante complicada - Me pareció mejor manera de manejar todo dibujo dentro del evento DrawSubItem, también es mejor para llevar a cabo su propio doble búfer mediante la clase BufferedGraphics.

También encontré el código fuente de ObjectListView muy útil.

Finalmente, todo esto es solo para manejar el modo de detalles de la vista de lista (el único modo que estoy usando), si quieres que los otros modos también funcionen, creo que hay cosas adicionales que tener en cuenta .

Cuando tenga la oportunidad, intentaré y publicaré mi código de ejemplo de trabajo.

3

no sé si esto le ayudará por completo, pero voy a añadir algunas notas:

Una cosa a tener en cuenta es que DrawSubItem también dibujará el primer elemento, y es probable que de allí obtenga el aspecto double-rendered.

Algunas cosas para probar (no factorizar para la velocidad):

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) { 
    e.DrawBackground(); 
    if ((e.State & ListViewItemStates.Selected) == ListViewItemStates.Selected) { 
    Rectangle r = new Rectangle(e.Bounds.Left + 4, e.Bounds.Top, TextRenderer.MeasureText(e.Item.Text, e.Item.Font).Width, e.Bounds.Height); 
    e.Graphics.FillRectangle(SystemBrushes.Highlight, r); 
    e.Item.ForeColor = SystemColors.HighlightText; 
    } else { 
    e.Item.ForeColor = SystemColors.WindowText; 
    } 
    e.DrawText(); 
    e.DrawFocusRectangle(); 
} 

Por su rutina DrawSubItem, asegurarse de que no está dibujando en la primera columna y añaden la rutina DrawBackground(). Agregué un recorte al rectángulo resaltado para que no se pintara fuera de los parámetros de la columna.

private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { 
    if (e.ColumnIndex > 0) { 
    e.DrawBackground(); 

    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 

    if (index >= 0) { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 

     e.Graphics.SetClip(e.Bounds); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
     e.Graphics.ResetClip(); 
    } 

    e.DrawText(); 
    } 
} 

En general, el propietario del dibujo de un control ListView es acogedor en un mundo de dolor. Ya no estás dibujando en Estilos Visuales, tendrías que hacer eso tú también. Ugh.

2

El color de la parte trasera seleccionada ha cambiado. In por defecto azul en windows. Este código le ayudará a u en cualquier color:

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     for (int ix = 0; ix < listView1.Items.Count; ++ix) 
     { 
      var item = listView1.Items[ix]; 
      item.BackColor = (ix % 2 == 0) ? Color.Gray : Color.LightGray; 

     } 

    } 

    private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
    { 
     e.DrawDefault = true; 
    } 

    } 
} 
0
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    } 
Cuestiones relacionadas