How to display a different value for dropdown list values/selected item in a WPF ComboBox?
Update 2011-11-14
I recently came upon the same requirement again and I wasn't very happy with the solution I posted below. Here is a nicer way to get the same behavior without re-templating the ComboBoxItem
. It uses a DataTemplateSelector
First, specify the regular DataTemplate
, the dropdown DataTemplate
and the ComboBoxItemTemplateSelector
in the resources for the ComboBox
. Then reference the ComboBoxItemTemplateSelector
as a DynamicResource
for ItemTemplateSelector
<ComboBox ...
ItemTemplateSelector="{DynamicResource itemTemplateSelector}">
<ComboBox.Resources>
<DataTemplate x:Key="selectedTemplate">
<TextBlock Text="{Binding Path=ShortDescription}"/>
</DataTemplate>
<DataTemplate x:Key="dropDownTemplate">
<TextBlock Text="{Binding Path=FullDescription}"/>
</DataTemplate>
<local:ComboBoxItemTemplateSelector
x:Key="itemTemplateSelector"
SelectedTemplate="{StaticResource selectedTemplate}"
DropDownTemplate="{StaticResource dropDownTemplate}"/>
</ComboBox.Resources>
</ComboBox>
ComboBoxItemTemplateSelector
checks if the container is the child of a ComboBoxItem
, if it is, then we are dealing with a dropdown item, otherwise it is the item in the ComboBox
.
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate DropDownTemplate
{
get;
set;
}
public DataTemplate SelectedTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ComboBoxItem comboBoxItem = VisualTreeHelpers.GetVisualParent<ComboBoxItem>(container);
if (comboBoxItem != null)
{
return DropDownTemplate;
}
return SelectedTemplate;
}
}
GetVisualParent
public static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
Old solution, requires re-templating of ComboBoxItem
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<ControlTemplate x:Key="FullDescriptionTemplate" TargetType="ComboBoxItem">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true">
<StackPanel>
<TextBlock Text="{Binding Path=FullDescription}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ComboBox Name="c_comboBox" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ShortDescription}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template" Value="{StaticResource FullDescriptionTemplate}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
This results in the following behavior
It doesn't seem to work for me now, but this one does:
public class ComboBoxItemTemplateSelector : DataTemplateSelector {
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate DropDownTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
var presenter = (ContentPresenter)container;
return (presenter.TemplatedParent is ComboBox) ? SelectedTemplate : DropDownTemplate;
}
}
I modified this custom rounded WPF ComboBox to display a different value from the item selected as well as change the color for each item.
Custom ComboBox
First you need to create the structure:
//Structure
public class COMBOITEM
{
string _ITEM_NAME;
string _ITEM_SHORT_NAME;
Brush _ITEM_COLOR;
public string ITEM_NAME
{
get { return _ITEM_NAME; }
set { _ITEM_NAME = value; }
}
public string ITEM_SHORT_NAME
{
get { return _ITEM_SHORT_NAME; }
set { _ITEM_SHORT_NAME = value; }
}
public Brush ITEM_COLOR
{
get { return _ITEM_COLOR; }
set { _ITEM_COLOR = value; }
}
}
Initialize the structure, fill it with data and bind to ComboBox:
private void Load_Data()
{
Brush Normal_Blue = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF1F4E79"));
//Load first entry
ObservableCollection<COMBOITEM> _Line_Data = new ObservableCollection<COMBOITEM>();
_Line_Data.Add(new COMBOITEM() { ITEM_NAME = "Line Number 1", ITEM_SHORT_NAME = "LN 1", ITEM_COLOR = Normal_Blue });
//Load Test Data
for (int i = 2; i < 10; i++)
{
_Line_Data.Add(new COMBOITEM()
{
ITEM_NAME = "Line Number " + i.ToString(),
ITEM_SHORT_NAME = "LN " + i.ToString(),
ITEM_COLOR = (i % 2 == 0) ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red) //This just changes color
});
}
//Bind data to combobox
cb_Test.ItemsSource = _Line_Data;
}
Now place the ComboBox in your design. To use it as a normal ComboBox, remove DisplayMemberPath and rename "ColorComboBoxItem" to "CustomComboBoxItem":
<ComboBox x:Name="cb_Test" FontSize="36" Padding="1,0" MinWidth="100" MaxWidth="400" Margin="5,53,10,207" FontFamily="Calibri" Background="#FFBFBFBF" Foreground="#FF1F4E79" BorderBrush="#FF1F4E79" VerticalContentAlignment="Center" TabIndex="5" IsSynchronizedWithCurrentItem="False"
Style="{DynamicResource RoundedComboBox}"
ItemContainerStyle="{DynamicResource ColorComboBoxItem}"
DisplayMemberPath="ITEM_SHORT_NAME" />
Now add the following styles/template to App.xaml Application.Resources:
<!-- Rounded ComboBox Button -->
<Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="2"
CornerRadius="8"
Background="{TemplateBinding Background}"
BorderBrush="#FF1F4E79"
BorderThickness="2"
/>
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="{TemplateBinding Foreground}"
Stroke="{TemplateBinding Foreground}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
<Border x:Name="PART_ContentHost" Focusable="True" />
</ControlTemplate>
<!-- ComboBox Template -->
<Style x:Key="RoundedComboBox" TargetType="{x:Type ComboBox}">
<Setter Property="Foreground" Value="#333" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="Background" Value="White" />
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="FontSize" Value="13" />
<Setter Property="MinWidth" Value="150"/>
<Setter Property="MinHeight" Value="35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Cursor="Hand"
Name="ToggleButton"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Style="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="10,3,30,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
CornerRadius="10"
x:Name="DropDownBorder"
Background="#FFBFBFBF"
BorderThickness="2"
BorderBrush="#FF1F4E79"
/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
<!--This style uses the normal items.add function-->
<Style x:Key="CustomComboBoxItem" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="30" />
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border
Name="Border"
Padding="5"
Margin="2"
BorderThickness="2,0,0,0"
CornerRadius="0"
Background="Transparent"
BorderBrush="Transparent">
<TextBlock TextAlignment="Left">
<ContentPresenter />
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="BorderBrush" Value="#FF3737CB"/>
<Setter TargetName="Border" Property="Background" Value="#FF6ACDEA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--This style uses the structure to fill items and set the item color-->
<Style x:Key="ColorComboBoxItem" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="30" />
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Foreground" Value="{Binding ITEM_COLOR}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border
Name="Border"
Padding="5"
Margin="2"
BorderThickness="2,0,0,0"
CornerRadius="0"
Background="Transparent"
BorderBrush="Transparent">
<TextBlock Text="{Binding ITEM_NAME}" TextAlignment="Left">
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="BorderBrush" Value="#FF3737CB"/>
<Setter TargetName="Border" Property="Background" Value="#FF6ACDEA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I hope this helps..