Selector (the class from which ListBox and ComboBox derives) defines a SelectedIndex property that indicates the index of the selected item, or the value is –1 if no item is currently selected. Selector also defines a SelectedItem property, which is the item itself, or null if there’s no selected item. If SelectedIndex is not equal to –1, SelectedItem is the same as the object returned from the Items property when indexed by SelectedIndex.
A SelectionChanged event is fired when the selection changes. This event implies that SelectedItem is a good choice for a binding source. SelectedItem is backed by a dependency property, so it can also serve as a binding target.
If a ListBox does not have its SelectedIndex or SelectedItem properties explicitly set, and the user has not yet touched the ListBox, SelectedIndex will be –1 and SelectedItem will be null. It’s helpful to prepare for these eventualities.
The ListBoxSelection program allows a user to pick a Color and a FontFamily from two ListBox controls and displays some text using those selections. The Resources collection contains a standard binding converter and a Style for the ListBox:
Example 1. Silverlight Project: ListBoxSelection File: MainPage.xaml (excerpt)
<phone:PhoneApplicationPage.Resources> <petzold:StringFormatConverter x:Name="stringFormat" />
<Style x:Key="listBoxStyle" TargetType="ListBox"> <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}" /> <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="Margin" Value="3" /> <Setter Property="Padding" Value="3" /> </Style> </phone:PhoneApplicationPage.Resources>
|
All three elements are in a three-row Grid:
Example 2. Silverlight Project: ListBoxSelection File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions>
. . .
</Grid>
|
The first ListBox contains a list of SolidColorBrush objects with the same DataTemplate used in the previous program to format the items:
Example 3. Silverlight Project: ListBoxSelection File: MainPage.xaml (excerpt)
<ListBox Name="brushListBox" Grid.Row="0" SelectedIndex="0" Style="{StaticResource listBoxStyle}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Rectangle Width="48" Height="36" Margin="2" Fill="{Binding}" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <TextBlock Text="{Binding Color.R, Converter={StaticResource stringFormat}, ConverterParameter=' {0:X2}'}" /> <TextBlock Text="{Binding Color.G, Converter={StaticResource stringFormat}, ConverterParameter='-{0:X2}'}" /> <TextBlock Text="{Binding Color.B, Converter={StaticResource stringFormat}, ConverterParameter='-{0:X2}'}" /> </StackPanel> </StackPanel> </DataTemplate> </ListBox.ItemTemplate>
<SolidColorBrush Color="AliceBlue" /> <SolidColorBrush Color="AntiqueWhite" /> <SolidColorBrush Color="Aqua" /> <SolidColorBrush Color="Aquamarine" /> <SolidColorBrush Color="Azure" /> . . . <SolidColorBrush Color="Wheat" /> <SolidColorBrush Color="White" /> <SolidColorBrush Color="WhiteSmoke" /> <SolidColorBrush Color="Yellow" /> <SolidColorBrush Color="YellowGreen" /> </ListBox>
|
Notice that SelectedIndex is explicitly set to 0 so that the ListBox will have a valid SelectedItem at startup.
The second ListBox displays font families. I would have preferred using actual FontFamily objects but they cannot be created in XAML because FontFamily does not have a parameterless constructor. Instead, I stored the names as strings. SelectedIndex is initialized at 5, a number I chose pretty much at random.
When you see a ListBox displaying font families, do you expect the names to be displayed in the actual fonts? That’s easy to implement with DataTemplate. Just bind both the Text and the FontFamily properties of a TextBlock to the items in the ListBox:
Example 4. Silverlight Project: ListBoxSelection File: MainPage.xaml (excerpt)
<ListBox Name="fontFamilyListBox" Grid.Row="1" SelectedIndex="5" Style="{StaticResource listBoxStyle}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" FontFamily="{Binding}" /> </DataTemplate> </ListBox.ItemTemplate> <system:String>Arial</system:String> <system:String>Arial Black</system:String> <system:String>Calibri</system:String> <system:String>Comic Sans MS</system:String> <system:String>Courier New</system:String> <system:String>Georgia</system:String> <system:String>Lucida Sans Unicode</system:String> <system:String>Portable User Interface</system:String> <system:String>Segoe WP</system:String> <system:String>Segoe WP Black</system:String> <system:String>Segoe WP Bold</system:String> <system:String>Segoe WP Light</system:String> <system:String>Segoe WP Semibold</system:String> <system:String>Segoe WP SemiLight</system:String> <system:String>Tahoma</system:String> <system:String>Times New Roman</system:String> <system:String>Trebuchet MS</system:String> <system:String>Verdana</system:String> <system:String>Webdings</system:String> </ListBox>
|
Because the items in the ListBox are strings rather than FontFamily objects, I wasn’t sure the binding to FontFamily in the template would work, but it did.
The XAML file concludes with a TextBlock not in any template at all. Two of its properties are binding targets referencing the two ListBox controls:
Example 5. Silverlight Project: ListBoxSelection File: MainPage.xaml (excerpt)
<TextBlock Grid.Row="2" Text="Sample Text" FontSize="{StaticResource PhoneFontSizeExtraLarge}" HorizontalAlignment="Center" Margin="12"
Foreground="{Binding ElementName=brushListBox, Path=SelectedItem}" FontFamily="{Binding ElementName=fontFamilyListBox, Path=SelectedItem}" />
|
When I was first developing this program, it seemed like the FontFamily binding in the DataTemplate was working fine but the FontFamily binding on the bottom TextBlock was causing a nasty runtime exception. I wrote a StringToFontFamilyConverter (which is still in the Petzold.Phone.Silverlight library) but the problem really seemed to be related to a SelectedItem value of null from the ListBox. Once I fixed that problem by explicitly initializing SelectedIndex, the binding problem disappeared.
As you play with this program you’ll see that the TextBlock changes height somewhat as the FontFamily changes. This has a ripple effect by causing changes to the two ListBox heights. A ListBox can also be susceptible to changes in width. If HorizontalAlignment is not Stretch, a ListBox will be only as wide as it needs to be, but because a ListBox uses a VirtualizingStackPanel by default, visual trees for items are created only as they are needed to be displayed. The ListBox might not know the width of its widest item at all times. It can be very disconcerting to see a ListBox change width as you scroll through the items!
For these reasons, a ListBox is often given an explicit width and height, or a specific width and height is imposed through a Grid.