BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Windows Presentation Foundation
.NET 4.0+

WPF Base Classes - Selector

The thirty-sixth part of the Windows Presentation Foundation Fundamentals tutorial looks at another WPF control base class. This article describes the Selector class, which is subclassed by controls that allow the user to select from a list of values.

SelectedItem Property

SelectedItem performs a similar function to SelectedIndex, as it allows you to determine the current selection or change it. However, rather than returning the index of the selected item, it returns the value itself. In the case of the TabControl, this is a TabItem reference. For other controls, which may be bound to any type of data, it will be the underlying object or value. If nothing is selected the property returns null. Setting it to null clears the current selection.

The following code uses SelectedItem to change both the content and the colour of the label to match the content and colour of the selected tab. When you click the button, the selected item is read directly from the TabControl and is cast to a TabItem so that the Content and Background properties can be read.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var tab = MyTabControl.SelectedItem as TabItem;
    SelectedTabLabel.Content = tab.Header;
    SelectedTabLabel.Background = tab.Background;
}

SelectedValue and SelectedValuePath Properties

The SelectedValue and SelectedValuePath properties work together. SelectedValue returns a value that is based upon the currently selected item, or the first item for controls that allow multiple selections. The returned value is not the index of the selected item or a reference to the item itself. It is determined by the contents of SelectedValuePath.

SelectedValuePath often contains the name of a property that is defined by the type of data in the collection. For example, when interrogating TabItems in a TabControl, you might set the SelectedValuePath to "Header". In this case, the SelectedValue property returns the Header value of the selected tab. You can also reference nested properties by building a path to the desired value. The path is made up of the property names, separated using a full stop (period) character.

To demonstrate, let's set the SelectedValuePath of the TabControl to "Header.Length". This two-part path means that querying the SelectedValue property will return the length, in characters, of the string within the selected tab's header.

<TabControl Name="MyTabControl" Grid.ColumnSpan="3" SelectedValuePath="Header.Length">

To read the property when the button is clicked, change the Click event's method as shown below. Run the program and try changing tabs and clicking the button to see the results.

private void Button_Click(object sender, RoutedEventArgs e)
{
    SelectedTabLabel.Content = MyTabControl.SelectedValue;
}

NB: SelectedValue is a writeable property. If you modify its value, the selection will be updated to one of the items that have the new value. If no such item exists, the selection will be cleared.

SelectionChanged Event

Clicking a button to use the properties is unnecessary, as there are several events that allow you to detect when the selection is changed. The first of these is SelectionChanged. This event is raised whenever the selected items in the collection are changed or when the selection is cleared.

To demonstrate, replace the XAML for the entire window with the revised version below. This removes the button and defines the SelectionChanged event for the TabControl.

<Window x:Class="SelectorDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsControl Demo"
        Width="250"
        Height="150">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <TabControl Name="MyTabControl" Grid.ColumnSpan="3"
                    SelectionChanged="MyTabControl_SelectionChanged">
            <TabItem Header="Red" Background="Red"/>
            <TabItem Header="Yellow" Background="Yellow"/>
            <TabItem Header="Green" Background="Green"/>
            <TabItem Header="Blue" Background="Blue"/>
        </TabControl>

        <Label Grid.Row="1">Selected:</Label>
        <Label Grid.Row="1" Grid.Column="1" Name="SelectedTabLabel"/>
    </Grid>
</Window>

Switch to the code behind the window and remove the Click event for the button that no longer exists. Add instead the following, which updates the label's content and colour when the tab selection changes.

private void MyTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var tab = MyTabControl.SelectedItem as TabItem;
    SelectedTabLabel.Content = tab.Header;
    SelectedTabLabel.Background = tab.Background;
}

As the TabControl is named and is the only control linked to this event, it's referenced directly in this case. However, if you are using the same event code for several controls, you can determine which control raised the event using the sender parameter. The second parameter provides event arguments holding details of the event. You can use this to find out which items were added to, and removed from, the selections. In this demonstration we don't need this information as we are reading the selected item directly.

Try running the program and switching tabs to see that the label is updated automatically.

16 November 2013