Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / WPF

Nullable ComboBox for Silverlight and WPF

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
30 Mar 2011CPOL1 min read 30.6K   940   11   4
This post describes a simple and easy way to change the current behavior of your Silverlight or WPF ComboBox by using an attached property and a ComboBox style.

Introduction

This article describes a simple and easy way to change the current behavior of your Silverlight or WPF ComboBox by using an attached property and a ComboBox style.

Here is a live example of the Silverlight version.

All that you need to do is set the IsNullable property to True on the ComboBox that you want to have this behavior:

XML
<ComboBox ItemsSource="{Binding Source={StaticResource ViewModel}, Path=Actors}"
          library:Combobox.IsNullable="True" />

Using the code

First, you need to change the default ComboBox style so that it will contain the X button. You can find the default style for Silverlight here and for WPF here. You need to make the following changes to the Template property of the ComboBox:

Silverlight ComboBox:
XML
<ContentPresenter x:Name="ContentPresenter" Margin="{TemplateBinding Padding}"  
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
    <TextBlock Text=" " />
</ContentPresenter>
<Button x:Name="PART_ClearButton" Visibility="Collapsed" 
    HorizontalAlignment="Right" Margin="0,0,20,0">
    <Path Data="M0,0 L1.6,0 L3,1.56 L4.4,0 L6,0 L3.8,2.5 L6,5 L4.4,5 L3,
                3.49 L1.59,5 L-4.2E-09,5 L2.18,2.5 z" 
      Fill="#CC111111" Height="5" 
      Stretch="Fill" Width="7"/>
</Button>
WPF ComboBox:
XML
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}"
        Template="{StaticResource ComboBoxTextBox}"
        HorizontalAlignment="Left"
        VerticalAlignment="Bottom"
        Margin="3,3,23,3"
        Focusable="True"
        Background="Transparent"
        Visibility="Hidden"
        IsReadOnly="{TemplateBinding IsReadOnly}" />
<Button x:Name="PART_ClearButton" 
        Visibility="Collapsed" 
        HorizontalAlignment="Right" 
        Margin="0,0,22,0">
    <Path Data="M0,0 L1.6,0 L3,1.56 L4.4,0 L6,0 L3.8,2.5 L6,5 L4.4,
                5 L3,3.49 L1.59,5 L-4.2E-09,5 L2.18,2.5 z" 
      Fill="#CC111111" Height="5" 
      Stretch="Fill" Width="7"/>
</Button>

When you have completed these changes, you need to create the IsNullable attached property that will do all the work. When this property is set to True on a ComboBox, it will enable the nullable functionality.

The nullable functionality is obtained by using these two functions:

C#
private static void ApplyIsNullable(ComboBox comboBox)
{
    var isNullable = GetIsNullable(comboBox);
    var clearButton = (Button)GetClearButton(comboBox);
    if (clearButton != null)
    {
        clearButton.Click -= clearButton_Click;
        clearButton.Click += clearButton_Click;

        if (isNullable && comboBox.SelectedIndex != -1)
        {
            clearButton.Visibility = Visibility.Visible;
        }
        else
        {
            clearButton.Visibility = Visibility.Collapsed;
        }
    }
}
 
private static void clearButton_Click(object sender, RoutedEventArgs e)
{
    var clearButton = (Button)sender;
    var parent = VisualTreeHelper.GetParent(clearButton);

    while (!(parent is ComboBox))
    {
        parent = VisualTreeHelper.GetParent(parent);
    }

    var comboBox = (ComboBox)parent;
    //clear the selection
    comboBox.SelectedIndex = -1;
}

The ApplyIsNullable function refreshes the X button state and is called whenever the selection of the ComboBox changes. The clearButton_Click function clears the ComboBox selection and is called when someone clicks the X button.

Before we finish, let's make the X button look better by adding a MouseOver animation by using this custom button style:

XML
<Style x:Key="ClearSelectionButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="#FF3C688D"/>
    <Setter Property="BorderBrush" Value="#FF617584"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Width" Value="15"/>
    <Setter Property="Height" Value="15"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid Background="Transparent">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Duration="0" 
                                          Storyboard.TargetProperty="(UIElement.Visibility)" 
                                          Storyboard.TargetName="MouseOverElement">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Duration="0" 
                                           Storyboard.TargetProperty="(UIElement.Visibility)" 
                                           Storyboard.TargetName="MouseOverElement">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled"/>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused"/>
                            <VisualState x:Name="Unfocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="MouseOverElement" 
                        BorderThickness="{TemplateBinding BorderThickness}" 
                        Background="#FFC8E4ED" BorderBrush="#FF3F6A8E" 
                        Visibility="Collapsed"/>
                    <ContentPresenter x:Name="contentPresenter" 
                      ContentTemplate="{TemplateBinding ContentTemplate}" 
                      Content="{TemplateBinding Content}" 
                      HorizontalAlignment="Center" 
                      Margin="{TemplateBinding Padding}" 
                      VerticalAlignment="Center"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

That's it! :)

History

  • Version 1.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Romania Romania
Mircea Deliu was born on August 17, 1982. He has been involved with computers in one way or another since high school, but started professionally in 2004. He has focused mostly on Microsoft .Net technologies and it's now working as a Senior Software Developer.

He likes to think about himself as an open-minded person with a good sense of humor. His hobbies involve traveling, driving his car and listening to music (all at once when possible). When he is not working or writing on his blog you can find him in the company of his wife and surrounded by their friends and family.

Comments and Discussions

 
QuestionSelectionChanged Pin
YakudzaBY6-Dec-11 11:12
YakudzaBY6-Dec-11 11:12 
AnswerRe: SelectionChanged Pin
Mircea Deliu13-Dec-11 6:20
Mircea Deliu13-Dec-11 6:20 
Generalvs 2008 Pin
TacosAndBananas11-May-11 4:21
TacosAndBananas11-May-11 4:21 
GeneralRe: vs 2008 Pin
Mircea Deliu16-May-11 22:30
Mircea Deliu16-May-11 22:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.