This article is currently in the process of being translated into Spanish (~99% done).
The ListBox control
En el artículo anterior hemos echado un vistazo al ItemsControl, el cual es probablemente la lista más simple en WPF. El control ListBox es el siguiente en la lista, el cual agrega un poco más de funcionalidad. Una de las principales diferencias es el hecho de que el control ListBox trabaja con selecciones, permitiendo al usuario final seleccionar uno o varios elementos de la lista y automáticamente da retroalimentación visual.
Aquí hay un ejemplo de un control ListBox muy simple.
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSample" Height="120" Width="200">
<Grid Margin="10">
<ListBox>
<ListBoxItem>ListBox Item #1</ListBoxItem>
<ListBoxItem>ListBox Item #2</ListBoxItem>
<ListBoxItem>ListBox Item #3</ListBoxItem>
</ListBox>
</Grid>
</Window>
Este ejemplo es tan simple como esto: Declaramos un control ListBox, dentro del cual declaramos tres ListBoxItem, cada uno con su propio texto. Sin embargo, ya que el ListBoxItem es un ContentControl, podemos definir contenido personalizado para este:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSample" Height="120" Width="200">
<Grid Margin="10">
<ListBox>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
<TextBlock>ListBox Item #1</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_green.png" />
<TextBlock>ListBox Item #2</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfTutorialSamples;component/Images/bullet_red.png" />
<TextBlock>ListBox Item #3</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
</Grid>
</Window>
Ahora, para cada uno de los ListBoxItem agregamos un StackPanel, en el cual agregamos una imagen y un TextBlock. Esto nos da control total del contenido, así como del renderizado, como puedes ver en la captura de pantalla, donde se han utilizado colores diferentes para cada uno de los números.
De la captura de pantalla también debes notar otra diferencia cuando se compara el ItemsControl con el ListBox: De manera predeterminada se muestra un borde alrededor del control, haciéndolo ver como un control actual, en vez de sólo una salida.
Vinculando datos al ListBox
DEfinir manualmente elementos del ListBox es un buen ejercicio de ejemplo, pero muchas de las veces tus controles ListBox se definirán con elementos de una fuente de datos usando la vinculación de datos. De manera predeterminada, si tú vinculas una lista de elementos al ListBox, se utilizará su método ToString() para representar cada elemento. Rara vez querrás lo anterior, pero afortunadamente podemos declarar fácilmente una plantilla que será usada para renderizar cada elemento.
He reutilizado el ejemplo basado en la lista POR HACER del artículo ItemsControl, donde construimos una lista POR HACER usando el código de una clase simple, en este caso, un control ListBox, para la representación visual. Aquí está el ejemplo:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxDataBindingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxDataBindingSample" Height="150" Width="300">
<Grid Margin="10">
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;
namespace WpfTutorialSamples.ListBox_control
{
public partial class ListBoxDataBindingSample : Window
{
public ListBoxDataBindingSample()
{
InitializeComponent();
List<TodoItem> items = new List<TodoItem>();
items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });
lbTodoList.ItemsSource = items;
}
}
public class TodoItem
{
public string Title { get; set; }
public int Completion { get; set; }
}
}
Toda la magia sucede en el ItemTemplate que hemos definido para el ListBox. Ahí, especificamos que cada elemento del ListBox debe consistir de un Grid, dividido en dos columnas: la primera con un TextBlock mostrando el título, y la segunda con una barra de progreso mostrando el estado de avance. Para obtener los valores de salida utilizamos una vinculación de datos muy simple, la cual es explicada en la sección de vinculación de datos de este tutorial.
En el archivo de código subyacente, hemos declarado una clase TodoItem muy simple para contener cada uno de nuestros elementos TODO. En el constructor de la ventana, inicializamos una lista, agregue tres elementos TODO y luego asígnelo a ItemsSource de ListBox. La combinación de ItemsSource y ItemTemplate que especificamos en la parte XAML, esto es todo lo que WPF necesita para representar todos los elementos como una lista TODO.
Tenga en cuenta la propiedad HorizontalContentAlignment que configuré en Stretch en ListBox. La alineación de contenido predeterminada para un elemento de ListBox es Izquierda , lo que significa que cada elemento solo ocupa tanto espacio horizontal como sea necesario. ¿El resultado? Bueno, no es del todo lo que nosotros queremos:
Al usar la alineación Stretch, cada elemento se estira para ocupar la cantidad total de espacio disponible, como puede ver en la captura de pantalla anterior.
Trabajando con la selección ListBox.
Como se mencionó, una diferencia clave entre ItemsControl y ListBox es que ListBox maneja y muestra la selección de usuario por usted. Por lo tanto, muchas preguntas de ListBox giran en torno a trabajar de alguna manera con la selección. Para ayudar con algunas de estas preguntas, he creado un ejemplo más grande, mostrándole algunos trucos relacionados con la selección:
<Window x:Class="WpfTutorialSamples.ListBox_control.ListBoxSelectionSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSelectionSample" Height="250" Width="450">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
</StackPanel.Resources>
<TextBlock FontWeight="Bold" Margin="0,0,0,10">ListBox selection</TextBlock>
<Button Name="btnShowSelectedItem" Click="btnShowSelectedItem_Click">Show selected</Button>
<Button Name="btnSelectLast" Click="btnSelectLast_Click">Select last</Button>
<Button Name="btnSelectNext" Click="btnSelectNext_Click">Select next</Button>
<Button Name="btnSelectCSharp" Click="btnSelectCSharp_Click">Select C#</Button>
<Button Name="btnSelectAll" Click="btnSelectAll_Click">Select all</Button>
</StackPanel>
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch" SelectionMode="Extended" SelectionChanged="lbTodoList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;
namespace WpfTutorialSamples.ListBox_control
{
public partial class ListBoxSelectionSample : Window
{
public ListBoxSelectionSample()
{
InitializeComponent();
List<TodoItem> items = new List<TodoItem>();
items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });
lbTodoList.ItemsSource = items;
}
private void lbTodoList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if(lbTodoList.SelectedItem != null)
this.Title = (lbTodoList.SelectedItem as TodoItem).Title;
}
private void btnShowSelectedItem_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.SelectedItems)
MessageBox.Show((o as TodoItem).Title);
}
private void btnSelectLast_Click(object sender, RoutedEventArgs e)
{
lbTodoList.SelectedIndex = lbTodoList.Items.Count - 1;
}
private void btnSelectNext_Click(object sender, RoutedEventArgs e)
{
int nextIndex = 0;
if((lbTodoList.SelectedIndex >= 0) && (lbTodoList.SelectedIndex < (lbTodoList.Items.Count - 1)))
nextIndex = lbTodoList.SelectedIndex + 1;
lbTodoList.SelectedIndex = nextIndex;
}
private void btnSelectCSharp_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.Items)
{
if((o is TodoItem) && ((o as TodoItem).Title.Contains("C#")))
{
lbTodoList.SelectedItem = o;
break;
}
}
}
private void btnSelectAll_Click(object sender, RoutedEventArgs e)
{
foreach(object o in lbTodoList.Items)
lbTodoList.SelectedItems.Add(o);
}
}
public class TodoItem
{
public string Title { get; set; }
public int Completion { get; set; }
}
}
Como puede ver, he definido un rango de botones a la derecha del ListBox, para obtener o manipular la selección. También he cambiado el Modo de selección a Extendido , para permitir la selección de múltiples elementos. Esto se puede hacer mediante programación, como yo hago en el ejemplo, o por el usuario final, presionando [Ctrl] o [Shift] mientras hace clic en los elementos.
Para cada uno de los botones, he definido un controlador de clics en el Código subyacente. Cada acción debe explicarse por sí misma y el código C# utilizado es bastante simple, pero si todavía tiene dudas, intente ejecutar el ejemplo en su propia máquina y pruebe las diversas posibilidades en el ejemplo.
Resumen
El control ListBox es muy similar al ItemsControl y se pueden usar varias de las mismas técnicas. ListBox ofrece un poco más de funcionalidad si lo comparamos con el ItemsControl, especialmente el manejo de la selección. Para obtener aún más funcionalidad, como los encabezados de columna, debe echar un vistazo al control ListView, que proporciona una descripción muy detallada más adelante en este tutorial con varios artículos que explican toda la funcionalidad.