En este artículo voy a mostrar cómo se puede personalizar la apariencia individual de cada elemento de un control
ListBox.
El objetivo final es conseguir un
ListBox con esta apariencia.
Para construir el ejemplo he creado un nuevo proyecto usando la plantilla de Aplicación de Windows Forms y he añadido un control
ListBox al formulario
Form1 que Visual Studio crea por defecto.
Para cargar los datos en el
ListBox he creado una nueva clase
Item que definirá cada uno de los elementos a añadir al
ListBox con dos propiedades:
Text que establecerá el texto a mostrar y
Category que nos permitirá identificar el tipo de elemento y darle un formato diferente según su valor.
En el evento
Load del formulario creo una lista de elementos
Item y se lo asigno a la propiedad
DataSource del
ListBox.
class Item
{
public Item(string itemText, string itemType)
{
Text = itemText;
Category = itemType;
}
public string Text { get; set; }
public string Category { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
List<Item> ListaItems = new List<Item>();
ListaItems.Add(new Item("Píldoras .NET", "Título"));
ListaItems.Add(new Item("Asier Villanueva", "Autor"));
ListaItems.Add(new Item("ASP.NET", "Sección"));
ListaItems.Add(new Item("MVC. Personalizar el Motor de Vistas de MVC", "Artículo"));
ListaItems.Add(new Item("MVC. Obtener el código generado por una vista Razor cshtml/vbhtml", "Artículo"));
ListaItems.Add(new Item("MVC. Plantilla de editor para fecha y hora", "Artículo"));
ListaItems.Add(new Item("Localizando Data Annotations", "Artículo"));
ListaItems.Add(new Item("Windows Forms", "Sección"));
ListaItems.Add(new Item("DataGridView. Columna con editor de fechas.", "Artículo"));
ListaItems.Add(new Item("DataGridView. Columna que acepta sólo números.", "Artículo"));
ListaItems.Add(new Item("Control TextBox con Botón", "Artículo"));
ListaItems.Add(new Item("MDI Child sin ControlBox", "Artículo"));
ListBox1.DataSource = ListaItems;
ListBox1.DisplayMember = "Text";
ListBox1.ValueMember = "Text";
}
Class Item
Public Sub New(itemText As String, itemType As String)
Text = itemText
Category = itemType
End Sub
Public Property Text As String
Public Property Category As String
End Class
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ListaItems = New List(Of Item)
ListaItems.Add(New Item("Píldoras .NET", "Título"))
ListaItems.Add(New Item("Asier Villanueva", "Autor"))
ListaItems.Add(New Item("ASP.NET", "Sección"))
ListaItems.Add(New Item("MVC. Personalizar el Motor de Vistas de MVC", "Artículo"))
ListaItems.Add(New Item("MVC. Obtener el código generado por una vista Razor cshtml/vbhtml", "Artículo"))
ListaItems.Add(New Item("MVC. Plantilla de editor para fecha y hora", "Artículo"))
ListaItems.Add(New Item("Localizando Data Annotations", "Artículo"))
ListaItems.Add(New Item("Windows Forms", "Sección"))
ListaItems.Add(New Item("DataGridView. Columna con editor de fechas.", "Artículo"))
ListaItems.Add(New Item("DataGridView. Columna que acepta sólo números.", "Artículo"))
ListaItems.Add(New Item("Control TextBox con Botón", "Artículo"))
ListaItems.Add(New Item("MDI Child sin ControlBox", "Artículo"))
ListBox1.DataSource = ListaItems
ListBox1.DisplayMember = "Text"
ListBox1.ValueMember = "Text"
End Sub
Por supuesto que si arrancamos la aplicación en este punto se añadirán los elementos al
ListBox pero la apariencia está muy lejos de la que deseamos:
Para poder personalizar la apariencia de cada elemento debemos cambiar el valor de la propiedad
DrawMode del
ListBox. Podemos asignar a la propiedad cualquiera de los valores de la enumeración
DrawMode:
- DrawMode.Normal: Todos los elementos se dibujan con la misma apariencia. Es el valor por defecto.
- DrawMode.OwnerDrawFixed: Los elementos se dibujan manualmente, pudiendo personalizarse de manera independiente, pero todos tienen el mismo tamaño.
- DrawMode.OwnerDrawVariable: Los elementos se dibujan manualmente y pueden ser de diferente tamaño.
He establecido el valor de la propiedad DrawMode como OwnerDrawVariable para mostrar cómo podemos personalizar no sólo la apariencia si no también el tamaño de cada elemento.
El dibujado de cada elemento lo realizaremos capturando el evento DrawItem del ListBox. Así que he creado un controlador para el evento DrawItem en el que dibujo el elemento con diferente estilo, tamaño y color dependiendo del valor de la propiedad Category del elemento a dibujar.
El evento DrawItem recibe un parámetro DrawItemEventArgs que tiene propiedades desde las que podemos obtener el formato de fuente y colores con los que se dibujaría el elemento por defecto.
Además proporciona otras dos propiedades que nos van a resultar útiles: la propiedad Index que devuelve el índice del elemento del ListBox a dibujar y la propiedad Graphics que proporciona un objeto Graphics que se utilizará para dibujar el elemento. Contiene además dos métodos que nos permiten dibujar el fondo del elemento (DrawBackground) y el rectángulo que indica que un elemento tiene el foco (DrawFocusRectangle).
private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
Item currentItem = (Item)ListBox1.Items[e.Index];
Font customFont = e.Font;
Brush customBrush = new SolidBrush(e.ForeColor);
string displayText = currentItem.Text;
switch (currentItem.Category)
{
case "Título":
customFont = new Font("Arial", 14, FontStyle.Bold);
customBrush = Brushes.Orange;
break;
case "Autor":
customFont = new Font("Arial", 7);
break;
case "Sección":
customFont = new Font(e.Font.FontFamily, 10, FontStyle.Bold);
break;
default:
displayText = " " + displayText;
break;
}
e.DrawBackground();
e.Graphics.DrawString(displayText,
customFont, customBrush, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
Private Sub ListBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ListBox1.DrawItem
Dim currentItem As Item = ListBox1.Items(e.Index)
Dim customFont As Font = e.Font
Dim customBrush As Brush = New SolidBrush(e.ForeColor)
Dim displayText As String = currentItem.Text
Select Case currentItem.Category
Case "Título"
customFont = New Font("Arial", 14, FontStyle.Bold)
customBrush = Brushes.Orange
Case "Autor"
customFont = New Font("Arial", 7)
Case "Sección"
customFont = New Font(e.Font.FontFamily, 10, FontStyle.Bold)
Case Else
displayText = " " + displayText
End Select
e.DrawBackground()
e.Graphics.DrawString(displayText, _
customFont, customBrush, e.Bounds, StringFormat.GenericDefault)
e.DrawFocusRectangle()
End Sub
Si ahora arrancamos la aplicación podremos ver el efecto conseguido:
Le hemos dado el formato al texto de los items, pero el tamaño de cada uno de ellos se mantiene con el tamaño estándar por lo que se nos montan unos textos encima de otros.
Para poder indicar el tamaño de cada elemento individual del
ListBox deberemos crear un controlador para el evento
MeasureItem del
ListBox.
El controlador del evento
MeasureItem recibe un parámetro del tipo
MeasureItemEventArgs. Este objeto nos proporciona, además de las propiedades
Index y
Graphics ya mencionadas, dos propiedades para establecer el alto y el ancho del elemento:
ItemHeight e
ItemWidth.
private void ListBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
Item currentItem = (Item)ListBox1.Items[e.Index];
switch (currentItem.Category)
{
case "Título":
e.ItemHeight = 22;
break;
case "Sección":
e.ItemHeight = 18;
break;
}
}
Private Sub ListBox1_MeasureItem(sender As Object, e As MeasureItemEventArgs) Handles ListBox1.MeasureItem
Dim currentItem As Item = ListBox1.Items(e.Index)
Select Case currentItem.Category
Case "Título"
e.ItemHeight = 22
Case "Sección"
e.ItemHeight = 18
End Select
End Sub
Le he aumentado el tamaño a los elementos de las categorías "Título" y "Sección", manteniendo el resto con el tamaño estándar. Si arrancamos ahora la aplicación veremos que hemos conseguido el efecto pretendido:
No hay comentarios:
Publicar un comentario