Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use DisplayMemberPath in a ComboBoxItem template

Tags:

c#

wpf

xaml

Currently I've hardcoded the property (DisplayName) of the DataContext object that should be shown. But normally you specify this path in the ComboBox itself with the property DisplayMemberPath. How can I use the value specified by DisplayMemberPath as the property to bind against in the content presenter?

<Style TargetType="{x:Type ComboBoxItem}">
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Border x:Name="ItemBorder"
                            Padding="2,0"
                            BorderThickness="1"
                            CornerRadius="3">


                        <ContentPresenter Content="{Binding DisplayName}"/>


                    </Border>

                    <ControlTemplate.Triggers>
                        <Trigger SourceName="ItemBorder" Property="IsMouseOver" Value="True">
                            <Setter TargetName="ItemBorder" Property="Background" Value="{StaticResource LightBlueBackgroundBrush}"/>
                            <Setter TargetName="ItemBorder" Property="BorderBrush" Value="{StaticResource LightBlueBackgroundBrush}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

For reference, here is my ComboBox style.

<Style TargetType="{x:Type ComboBox}">
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="Height" Value="22"/>
    <Setter Property="Background" Value="White"/>
    <Setter Property="BorderBrush" Value="{StaticResource MidGreyBorderStroke}"/>
    <Setter Property="Border.CornerRadius" Value="3" />
    <Setter Property="IsEditable" Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ComboBox}">

                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="1"
                        CornerRadius="{TemplateBinding Border.CornerRadius}"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}">

                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>

                        <ContentPresenter x:Name="ReadOnlyContentPresenter"
                                          Grid.Column="0"
                                          Margin="5,0"
                                          VerticalAlignment="Center"
                                          HorizontalAlignment="Left"
                                          Content="{TemplateBinding SelectionBoxItem}"
                                          ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                                          ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" />

                        <TextBox x:Name="PART_EditableTextBox"
                                 Grid.Column="0"
                                 Margin="5,0"
                                 VerticalAlignment="Center"
                                 HorizontalAlignment="Center"
                                 Visibility="Hidden"
                                 IsReadOnly="{TemplateBinding IsReadOnly}" />

                        <Border Grid.Column="1"    
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="1,0,0,0"
                                Margin="0,2" />

                        <ToggleButton Grid.Column="2"
                                      Margin="1,0"
                                      Background="{TemplateBinding Background}"
                                      VerticalAlignment="Center"
                                      HorizontalAlignment="Right"
                                      Focusable="False"
                                      IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                      ClickMode="Press"
                                      Style="{StaticResource ComboBoxToggleButton}"/>

                        <Popup x:Name="PART_Popup"
                               AllowsTransparency="True"
                               Placement="Bottom"
                               IsOpen="{TemplateBinding IsDropDownOpen}"
                               Focusable="False"
                               PopupAnimation="Fade"
                               SnapsToDevicePixels="True">

                            <Grid Background="Transparent"
                                  MinWidth="{TemplateBinding ActualWidth}"
                                  MaxHeight="{TemplateBinding MaxDropDownHeight}">

                                <Border x:Name="DropDownBorder"
                                        Background="{TemplateBinding Background}"
                                        BorderBrush="{TemplateBinding BorderBrush}"
                                        BorderThickness="1"
                                        CornerRadius="{TemplateBinding Border.CornerRadius}"
                                        Margin="0,2,0,0">

                                    <ScrollViewer Margin="6,0">
                                        <StackPanel IsItemsHost="True"
                                                    KeyboardNavigation.DirectionalNavigation="Contained" />
                                    </ScrollViewer>
                                </Border>
                            </Grid>
                        </Popup>
                   </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Opacity" Value=".65" />
                    </Trigger>

                    <Trigger Property="HasItems" Value="false">
                        <Setter TargetName="DropDownBorder" Property="MinHeight" Value="42"/>
                    </Trigger>

                    <Trigger Property="IsEditable" Value="True">
                        <Setter Property="IsTabStop" Value="false"/>
                        <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
                        <Setter TargetName="ReadOnlyContentPresenter" Property="Visibility" Value="Hidden"/>
                    </Trigger>
                </ControlTemplate.Triggers>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
like image 941
Nathanael Avatar asked Mar 07 '13 23:03

Nathanael


1 Answers

Well this is actually an interesting question that has quite an simple answer.

Short answer:

   <ContentPresenter />

Long answer:

If you have overridden the ComboBoxItem style but you still want to use DisplayMemberPath from the ComboBox you don't have to put anything into the Styles Content binding as the Parent(ComboBox) will resolve this for you. Because DisplayMemberPath is just a string not the actual property, if you Bind to DisplayMemberPath all your items will just show whatever value you put into DisplayMemberPath

So all you have to do is remove the Content binding from the ComboBoxItem Style

Example:

<Style TargetType="{x:Type ComboBoxItem}">
    .............
    <Border x:Name="ItemBorder" Padding="2,0"  BorderThickness="1"  CornerRadius="3">
        <ContentPresenter /> <!-- no content binding -->
    </Border>

Here is a simple example that shows this working

Xaml:

<Window x:Class="WpfApplication13.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" x:Name="UI" Width="343" Height="744.625" >
    <Window.Resources>
        <Style TargetType="{x:Type ComboBoxItem}">
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ComboBoxItem">
                        <Border x:Name="ItemBorder" Padding="2,0"  BorderThickness="1"  CornerRadius="3">
                            <ContentPresenter />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <StackPanel DataContext="{Binding ElementName=UI}">
        <ComboBox ItemsSource="{Binding Items}" DisplayMemberPath="Name" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"/>
        <ComboBox ItemsSource="{Binding Items}" DisplayMemberPath="State" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"/>
    </StackPanel>

</Window>

Code:

public partial class MainWindow : Window
{

    public MainWindow() 
    {
        InitializeComponent();

        for (int i = 0; i < 50; i++)
        {
            Items.Add(new ComboBoxModel { Name = "Name" + i, State = "State" + i });
        }
    }

    private ObservableCollection<ComboBoxModel> _items = new ObservableCollection<ComboBoxModel>();
    public ObservableCollection<ComboBoxModel> Items
    {
        get { return _items; }
        set { _items = value; }
    }

}

public class ComboBoxModel
{
    public string Name { get; set; }
    public string State { get; set; }
}

Result:

enter image description hereenter image description here

like image 150
sa_ddam213 Avatar answered Oct 21 '22 08:10

sa_ddam213