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

alt text


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