TOC

This article is currently in the process of being translated into Italian (~72% done).

Lista di controlli:

Il controllo ListBox

Nell'ultimo articolo, abbiamo dato un'occhiata all' ItemsControl, che è con ogni probabilità la lista più semplice in WPF. La ListBox è il controllo successivo che aggiunge una qualche funzionalità in più. Una delle principali differenze risiede nel fatto che la ListBox tratta attualmente con le selezioni, dando all'utente la possibilità di selezionare uno o più elementi nella lista e restituendo automaticamente il risultato visivo.

Ecco un esempio di controllo ListBox molto semplice:

<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>

Tutto ciò è semplice come appare: dichiariamo un controllo ListBox e al suo interno dichiariamo tre ListBoxItem, ognuno con il proprio testo. Tuttavia, poiché il ListBoxItem è in realtà un ContentControl, possiamo definire contenuti personalizzati per esso:

<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>

Per ciascuno degli elementi ListBoxItem ora aggiungiamo uno StackPanel, in cui aggiungiamo un'immagine e un blocco di testo. Questo ci dà il pieno controllo sul contenuto così come sul rendering di testo, come si può vedere dallo screenshot, in cui sono stati usati colori diversi per ciascuno dei numeri.

Dallo screenshot potresti anche notare un'altra differenza confrontando ItemsControl con ListBox: per impostazione predefinita, attorno al controllo viene mostrato un bordo, facendolo apparire effettivamente come un controllo anziché solo output.

Collegamendo dati al ListBox

La definizione manuale degli elementi per ListBox costituisce un ottimo primo esempio, ma la maggior parte delle volte, i controlli ListBox verranno riempiti con elementi provenienti da una origine dati utilizzando l'associazione (databinding). Per impostazione predefinita, se si associa un elenco di elementi a ListBox, il loro metodo ToString() verrà utilizzato per rappresentare visivamente ciascun elemento. Questo è raramente quello che si vuole, ma fortunatamente, possiamo dichiarare con facilità un modello che verrà usato per renderizzare ogni elemento.

Ho riutilizzato l'esempio basato su TODO nel paragrafo sugli ItemsControl, dove costruiamo un fantastico elenco TODO usando una semplice classe nel Code-behind e, in questo caso, un controllo ListBox per la rappresentazione visiva. Ecco l'esempio:

<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; }
	}
}

Tutta la magia accade nell'ItemTemplate che abbiamo definito per ListBox. Lì, specifichiamo che ogni elemento ListBox deve essere costituito da una griglia, diviso in due colonne, con un TextBlock che mostra il titolo nella prima colonna e un ProgressBar che mostra lo stato di completamento nella seconda colonna. Per estrarre il valore, usiamo un semplice abbinamento dati, che è tutto spiegato nella parte di associazione (dataBinding) di questo tutorial.

In the Code-behind file, we have declared a very simple TodoItem class to hold each of our TODO items. In the constructor of the window, we initialize a list, add three TODO items to it and then assign it to the ItemsSource of the ListBox. The combination of the ItemsSource and the ItemTemplate we specified in the XAML part, this is all WPF need to render all of the items as a TODO list.

Please notice the HorizontalContentAlignment property that I set to Stretch on the ListBox. The default content alignment for a ListBox item is Left, which means that each item only takes up as much horizontal space as it needs. The result? Well, not quite what we want:

By using the Stretch alignment, each item is stretched to take up the full amount of available space, as you can see from the previous screenshot.

Working with ListBox selection

As mentioned, a key difference between the ItemsControl and the ListBox is that the ListBox handles and displays user selection for you. Therefore, a lot of ListBox question revolves around somehow working with the selection. To help with some of these questions, I have created a bigger example, showing you some selection related tricks:

<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; }
	}
}

Come puoi vedere, ho definito una serie di pulsanti a destra di ListBox, per ottenere o manipolare la selezione. Ho anche modificato la modalità SelectionMode in Extended, per consentire la selezione di più elementi. Questo può essere fatto a livello di codice, come io ho fatto nell'esempio, o dall'utente finale, tenendo premuto [Ctrl] o [Maiusc] mentre si fa clic sugli elementi.

Per ciascuno dei pulsanti, ho definito un gestore dell'evento click nel codice. Ogni azione dovrebbe essere piuttosto esplicativa e il codice C# utilizzato è abbastanza semplice, ma se siete ancora in dubbio, provate a eseguire l'esempio sul vostro computer e provate le varie possibilità nell'esempio.

Riassunto

Il controllo ListBox è molto simile al controllo ItemsControl e possono essere utilizzate molte delle stesse tecniche. ListBox offre un po' più di funzionalità rispetto a ItemsControl, in particolare la gestione della selezione. Per ulteriori funzionalità, come le intestazioni di colonna, dovreste dare un'occhiata al controllo ListView, di cui viene data una descrizione molto approfondita più avanti in questo tutorial con diversi articoli che spiegano tutte le funzionalità.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!