Camron B
Camron B

Reputation: 1840

Custom Template for ComboBox ignores use of DisplayMemberPath

So I've gone through several questions on the site, and I cannot seem to find the answer.

I have a ComboBox. It was working just fine. I decide I need to overhaul the appearance, so I create a copy of the default ComboBox template (this is a straight copy, no modifications):

<ControlTemplate x:Key="ComboBoxControlTemplate2" TargetType="{x:Type ComboBox}">
            <Grid x:Name="MainGrid" SnapsToDevicePixels="True">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
                </Grid.ColumnDefinitions>
                <Popup x:Name="PART_Popup" AllowsTransparency="True" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                    <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
                        <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                            <ScrollViewer x:Name="DropDownScrollViewer">
                                <Grid RenderOptions.ClearTypeHint="Enabled">
                                    <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                        <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                                    </Canvas>
                                    <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Grid>
                            </ScrollViewer>
                        </Border>
                    </Microsoft_Windows_Themes:SystemDropShadowChrome>
                </Popup>
                <ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
                    <ToggleButton.Style>
                        <Style TargetType="{x:Type ToggleButton}">
                            <Setter Property="OverridesDefaultStyle" Value="True"/>
                            <Setter Property="IsTabStop" Value="False"/>
                            <Setter Property="Focusable" Value="False"/>
                            <Setter Property="ClickMode" Value="Press"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" SnapsToDevicePixels="True">
                                            <Grid HorizontalAlignment="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
                                                <Path x:Name="Arrow" Data="M0,0L3.5,4 7,0z" Fill="Black" HorizontalAlignment="Center" Margin="3,1,0,0" VerticalAlignment="Center"/>
                                            </Grid>
                                        </Microsoft_Windows_Themes:ButtonChrome>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="RenderPressed" TargetName="Chrome" Value="True"/>
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Fill" TargetName="Arrow" Value="#FFAFAFAF"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ToggleButton.Style>
                </ToggleButton>
                <ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="False" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
                    <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
                    <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
                </Trigger>
                <Trigger Property="HasItems" Value="False">
                    <Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    <Setter Property="Background" Value="#FFF4F4F4"/>
                </Trigger>
                <Trigger Property="IsGrouping" Value="True">
                    <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
                </Trigger>
                <Trigger Property="CanContentScroll" SourceName="DropDownScrollViewer" Value="False">
                    <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
                    <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

Now, when I select an item from my list (which is a collection of POCOs), it's display the namespace and class name instead of the value it's supposed to.

My research and experimentation have led me to believe that the problem is that my new template does not make use of the DisplayMemberPath property. I tried to set an ItemTemplate by overriding the OnDisplayMemberPathChanged method, but that results in errors when I select an item from the list.

I have also seen people set the ItemTemplate via XAML, but I have hundreds of comboboxes, and I don't want to do that.

Is there some way to utilize the DisplayMemberPath property in my ControlTemplate, or some code I could run in a derived control to achieve my desired result?

Upvotes: 13

Views: 6250

Answers (5)

Striver
Striver

Reputation: 471

Make sure you are not missing the ContentTemplateSelector elements.

For the combox template itself it should be:

ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

For the ItemsContainerStyle it should be:

ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"        

Upvotes: 3

Dan
Dan

Reputation: 163

I know this is an old question, but I faced the same problem in these days and I fixed it adding

ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"

instead of

ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

Upvotes: 0

RainCast
RainCast

Reputation: 4511

I met the same issue, this should be a bug, I'll try report it.

When you "edit a copy" of the control template from Visual Studio, it misses the line:

ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

Which caused "DisplayMemberPath" not working properly.

Upvotes: 0

brunnerh
brunnerh

Reputation: 185445

That is not an exact copy, one crucial thing is missing in this element:

<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
        Content="{TemplateBinding SelectionBoxItem}"
        ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        IsHitTestVisible="False"
        Margin="{TemplateBinding Padding}"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />

Maybe you accidentally deleted it, namely:

 ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

If you do not have this set the DisplayMemberPath will not work because the ComboBox selects between templates using a template selector (as you can use ItemTemplate or DisplayMemberPath).

Upvotes: 25

Rachel
Rachel

Reputation: 132618

I had the same question, and it turns out setting the DisplayMemberPath is simply a shortcut way for setting the ItemTemplate to a TextBlock with that value in it.

Because of this, when you set the ItemTemplate then DisplayMemberPath becomes useless because you have overwritten the default TextBlock with value that it adds.

Upvotes: 1

Related Questions