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