Click here to Skip to main content
Click here to Skip to main content

Nullable ComboBox for Silverlight and WPF

, 30 Mar 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

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

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:

<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! Smile | :)

History

  • Version 1.

License

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

Share

About the Author

Mircea Deliu
Software Developer (Senior) Senior Systems Europe
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 at Senior Systems Europe using Silverlight and WPF on a daily basis.
 
He likes to think about himself as an open-minded person with a good sense of humor. His hobbies involve traveling, driving his Honda Civic 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 Simona and surrounded by their friends and family.

Comments and Discussions

 
QuestionSelectionChanged [modified] PinmemberYakudzaBY6-Dec-11 12:12 
AnswerRe: SelectionChanged PinmemberMircea Deliu13-Dec-11 7:20 
Generalvs 2008 PinmemberTacosAndBananas11-May-11 5:21 
GeneralRe: vs 2008 PinmemberMircea Deliu16-May-11 23:30 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 30 Mar 2011
Article Copyright 2011 by Mircea Deliu
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid