Customizing WPF combo box style

By Mirek on (tags: combobox, CustomTemplate, style, WPF, categories: code)

It this post we will try to create a custom combo box style which adjust the look of standard control to our needs.

In general we will try to change this standard look of combobox

StandardComboBox

and achieve something like this

CoolComboBox

Great styling examples for combo box, buttons and tab items can be found on Codeplex, which I based my work on.
If you are not familiar with styling in wpf there is a lot of resources on the internet. I suggest starting on MSDN. For detailed information about changing the look and feel of the standard wpf controls go to Customizing the Appearance of an Existing Control by Creating a ControlTemplate.

As you can see on above picture we are going to change the look of the standard combo box totally. Even the small, right arrow is going to be changed. To achieve that we have to totally replace whole control style. Below I have put the skeleton of the XAML styles

   1: <ControlTemplate x:Key="CustomToggleButton" TargetType="ToggleButton">
   2:         <Grid>
   3:             <Border Name="Border" />
   4:             <Border Name="SmallBorder" />
   5:             <Path Name="Arrow" />
   6:         </Grid>
   7:     </ControlTemplate>
   8:  
   9:     <Style TargetType="{x:Type ComboBoxItem}">
  10:         <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
  11:         <Setter Property="Control.Template">
  12:             <Setter.Value>
  13:                 <ControlTemplate TargetType="{x:Type ComboBoxItem}">
  14:                     <Border>
  15:                         <ContentPresenter />
  16:                     </Border>
  17:                 </ControlTemplate>
  18:             </Setter.Value>
  19:         </Setter>
  20:     </Style>
  21:  
  22:     <Style TargetType="{x:Type ComboBox}">
  23:         <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
  24:         <Setter Property="Control.Template">
  25:             <Setter.Value>
  26:                 <ControlTemplate TargetType="ComboBox">
  27:                     <Grid>
  28:                         <ToggleButton Template="{StaticResource CustomToggleButton}" />
  29:                         <ContentPresenter />
  30:                         <TextBox />
  31:                         <Popup>
  32:                             <Grid>
  33:                                 <Border>
  34:                                     <ScrollViewer>
  35:                                         <ItemsPresenter />
  36:                                     </ScrollViewer>
  37:                                 </Border>
  38:                             </Grid>
  39:                         </Popup>
  40:                     </Grid>
  41:                 </ControlTemplate>
  42:             </Setter.Value>
  43:         </Setter>
  44:     </Style>

For the sake of clarity I have removed all less important setters and attributes. The most important setter, which is

   1: <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />

indicates that we are going to totally override the default style. It means that our combo box will not use standard theme style properties, but all style properties will come from styles provided by us. We must override all control styles since we want to change the shape of the arrow and thus change the look of the toggle button of the combo box.

In line 1. starts the control template for the ToggleButton which is the main container for combo box and all controls that can change its state. In our case it consists of two borders, one includes whole content and the second includes the text box only, and the path geometry which draws the arrow.

The ComboBoxItem template contains some triggers which changes the colors of highlighted items. These triggers are not visible here.

In line 22 you can see the style and control template for the combo box itself. It is built on grid and contains ToggleButton, ContentPresenter, TextBox. TextBox here is visible when the combo box is in editable mode.

In the XAML above you can see we create custom control templates for ComboBoxItem, ToggleButton and for whole ComboBox .

The structure of controls that are incorporated is shown below

ComboBoxStructure

The Path element is used to draw the arrow which looks as we expect. This is done in ToggleButton control template

 
   1: <ControlTemplate x:Key="CBCustomToggleButton" TargetType="ToggleButton">
   2:             <Grid>
   3:                 <Border Name="Border"
   4:                         BorderThickness="1,1,1,1"/>
   5:                 <Border Name="SmallBorder"
   6:                         BorderThickness="0,0,1,0" />
   7:                 <Path Name="Arrow"
   8:                       Width="10"
   9:                       Data="M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z"
  10:                       Fill="{StaticResource ActiveBorderBrush}" />
  11:             </Grid>
  12:             <ControlTemplate.Triggers>
  13:                 <Trigger Property="UIElement.IsMouseOver" Value="True">
  14:                     <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource ActiveBorderBrush}" />
  15:                     <Setter TargetName="SmallBorder" Property="BorderBrush" Value="{StaticResource ActiveBorderBrush}" />
  16:                     <Setter TargetName="Arrow" Property="Fill" Value="White" />
  17:                 </Trigger>
  18:                 <Trigger Property="ToggleButton.IsChecked" Value="True">
  19:                     <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource ActiveBorderBrush}" />
  20:                     <Setter TargetName="SmallBorder" Property="BorderBrush" Value="{StaticResource ActiveBorderBrush}" />
  21:                 </Trigger>
  22:                 <Trigger Property="UIElement.IsEnabled" Value="False">
  23:                     ...
  24:                 </Trigger>
  25:             </ControlTemplate.Triggers>
  26:         </ControlTemplate>
In line 9 we have used the StreamGeometry to provide the shape of our arrow which is then filled with solid color taken from application resources and called ActiveBorderBrush. In triggers section we set the border brush of both borders and fill color of the arrow depending on the state of the control.
 

The control template of the ToggleButton and ComboBoxItem are referenced in control template for our custom combo box, embedded in following style, presented in simplified version below

   1: <Style TargetType="{x:Type ComboBox}">
   2:           <Setter Property="Control.Template">
   3:               <Setter.Value>
   4:                   <ControlTemplate TargetType="ComboBox">
   5:                       <Grid>
   6:                           <ToggleButton Name="ToggleButton"
   7:                                         Template="{StaticResource CBCustomToggleButton}" />
   8:                           <ContentPresenter Name="ContentSite"
   9:                                             Content="{TemplateBinding ComboBox.SelectionBoxItem}"
  10:                                             ContentTemplate="{TemplateBinding ComboBox.SelectionBoxItemTemplate}"/>
  11:                           <TextBox x:Name="PART_EditableTextBox"
  12:                                    Foreground="{TemplateBinding Foreground}"
  13:                                    IsReadOnly="{TemplateBinding IsReadOnly}"
  14:                                    Visibility="Hidden" />
  15:                           <Popup Name="PART_Popup"
  16:                                  IsOpen="{TemplateBinding ComboBox.IsDropDownOpen}">
  17:                               <Grid Name="DropDown"
  18:                                     MinWidth="{TemplateBinding FrameworkElement.ActualWidth}"
  19:                                     MaxHeight="{TemplateBinding ComboBox.MaxDropDownHeight}"
  20:                                     SnapsToDevicePixels="True">
  21:                                   <Border Name="DropDownBorder"
  22:                                           Background="{TemplateBinding ComboBox.Background}">
  23:                                       <ScrollViewer Margin="4,6,4,6">
  24:                                           <ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" />
  25:                                       </ScrollViewer>
  26:                                   </Border>
  27:                               </Grid>
  28:                           </Popup>
  29:                       </Grid>
  30:                       <ControlTemplate.Triggers>
  31:                           ..
  32:                           <Trigger SourceName="PART_Popup" Property="Window.AllowsTransparency" Value="True">
  33:                               <Setter TargetName="DropDownBorder" Property="FrameworkElement.Margin" Value="0,2,0,0" />
  34:                           </Trigger>
  35:                           <Trigger Property="ComboBox.IsEditable" Value="True">
  36:                               <Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
  37:                               <Setter TargetName="PART_EditableTextBox" Property="UIElement.Visibility" Value="Visible" />
  38:                               <Setter TargetName="ContentSite" Property="UIElement.Visibility" Value="Hidden" />
  39:                           </Trigger>
  40:                       </ControlTemplate.Triggers>
  41:                   </ControlTemplate>
  42:               </Setter.Value>
  43:           </Setter>
  44:       </Style>

As described on MSDNThe logic of ComboBox expects to find a TextBox named PART_EditableTextBox and a Popup named PART_Popup in its ControlTemplate.”, which is defined by TemplatePartAttributes on the ComboBox class. We have to stick to this rule as long as we don’t want to loose the basic functionality of the control. You can see here in many places the use of TemplateBinding which allows us to reference dependency properties attached to the root control and assign their values in our template.

In the triggers section you can see that the text box is shown when the IsEditable property is set to true.

Leave a comment

Please log in to leave a comment. If you don't have an account, you can register here.

Ferhad
# FerhadJabiyev 9/29/2012 7:58 AM
Thanks for the article. This comboBox is the thing what I need. But i cant run project. I have worked very little with styling in WPF. Could you add the solution of project. Thanks.
sanyammittal2002
# sanyammittal2002 3/6/2013 1:06 PM
Thanks a lot for the article. I included your code in App.xaml file in my project. But it is not getting executed. It gives a runtime exception saying: XamlParseException was unhandled 'Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception.' I think I need to make changes at some other place as well for the overriding to take place properly. i'd be very grateful if you could help me out. Thanks.
Tomrei
# Tomrei 4/6/2013 7:59 AM
426029 [b]Posted by:[url=http://www.chaussuredefootpascherf50.com/]chaussures de foot nike[/url],[url=http://www.chaussuredefootballnike.com/]chaussures de foot[/url],[url=http://www.chaussuremercurialpascherfr.com/]chaussures de foot[/url],[url=http://www.chaussuresdefootpascheronline.com/]chaussures discount[/url],[url=http://www.mercurialvaporpascher99.org/]chaussures soldes[/url]; Links:[/b][url=http://www.Classmates.com/]chaussures de foot nike[/url] chaussures: golf training Buy a lot cheapest chaussures Shoes Black Red on Discount chaussures Shoes Sale US store enjoy more discount and free shipping. Research the juicers to learn how each is cleanedSome parts may or may not be able to go in the dishwasher and need to be washed by handDoes the machine come apart easily so all areas are accessible? If any parts of the machine can't be taken apart or are hard to reach to remove food particles 'We have very good quality price cheap woman being Chaussures de foot nike,chaussures: designer Many chaussures de foot chaussures Skyline. Red &chaussures, please come to our online store to buy!', please come to our online store to buy!', please come to our online store to buy!', please come to our online store to buy!',chaussures: with black &chaussures,chaussures: I wouldn`t be where I am today. So I`d say a lot most important thing when it comes to marketing is your contacts. Get as the many contacts as the you can &chaussures,
mganeshgan
# ganesh 5/6/2013 8:33 AM
hi... This is what exactly i need. I have tried your code. but it didnt work for me. please add your project file. or mail me at mganeshgan@gmail.com Thanks in advance.