This post describes a simple and easy way to change the current behavior of your Silverlight of WPF ComboBox by using an attached property and a ComboBox style.
Here is a Live Example of the Silverlight version:
As you can see the selection of the ComboBox can be easily cleared by pressing the X button.
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" />
Now let me tell you what you need to do to implement it in your application.
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 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.
Now all that you have to do is to apply the Combobox.IsNullable=”True” attached property to all your Nullable comboboxes.
Before we finish let’s make the X button look a 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!
Don’t forget to drop me a comment to let me know what you think.
You can download the sample application for Silverlight from here and for WPF from here.