12

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?

H.B.
  • 166,899
  • 29
  • 327
  • 400
Camron B
  • 1,650
  • 2
  • 14
  • 30

5 Answers5

23

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

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Dude! That totally worked. I never saw that solution though, and I'm 100% positive it was never generated. I have done this process 3 or 4 times, and checked scores of people with the same problem. This must totally be a bug on Microsoft's side... Thank you! – Camron B Nov 04 '11 at 20:50
  • Now that the joy has subsided, I must ask how you figured that out. What are you using to generate template copies? – Camron B Nov 04 '11 at 20:52
  • 3
    @CamronBute: Well, i always have the default templates that you can get [on MSDN](http://msdn.microsoft.com/en-us/library/aa970773.aspx) (`Default WPF Themes` link) at hand, so i checked it out to see how things are bound up. It isn't hard to see that this `ContentPresenter` is the key component for the selected item so if there is an error it should be there, then i just needed to compare it with yours to see that this property was missing. I also tested the effect of removing that property myself and as expected it only showed the type name. – H.B. Nov 04 '11 at 22:24
  • I have never seen that link before. I have always used Blend. Thank you for your help, and the link! – Camron B Nov 07 '11 at 22:12
  • 3
    I can confirm that the blend "copy template" has this quirk, even in VS2012. – dansan Sep 10 '13 at 20:16
  • Same thing happend with VS 2015 – jHilscher Mar 28 '17 at 12:24
  • In my situation I had the combo box template correct but the ItemsContainerStyle was missing the ContentTemplateSelector. Make sure to check out the answer below. – robaudas Mar 16 '18 at 20:40
3

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}"        
Striver
  • 451
  • 6
  • 7
1

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.

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Check the answer above. I saw your problem in a related question, but the above solution works for me! Thanks anyway, though :) – Camron B Nov 04 '11 at 20:49
  • I used the solution by H.B. but consequent to this answer, trying `ContentTemplate="{TemplateBinding ItemTemplate}"` also worked - though I didn't bother leaving it long enough to test for any side-effects. – OhBeWise Dec 10 '15 at 22:57
0

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.

RainCast
  • 4,134
  • 7
  • 33
  • 47
0

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}"

Dan
  • 163
  • 1
  • 8