I have a generic style for a ListBox
that overwrites the ItemTemplate
to use RadioButtons
. It works great, EXCEPT when I set a DisplayMemberPath
. Then I just get the .ToString()
of the item in the ListBox
.
I feel like I'm missing something simple here... can someone help me spot it?
<Style x:Key="RadioButtonListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2, 2, 2, 0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}" VerticalAlignment="Center"
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
My ListBox
is bound to a List<T>
of KeyValuePairs
. If I remove the Style, the DisplayMemberPath
shows up correctly so it must be something with the style.
<ListBox Style="{StaticResource RadioButtonListBoxStyle}"
ItemsSource="{Binding MyCollection}"
DisplayMemberPath="Value" SelectedValuePath="Key" />
Why exactly do you want to keep DisplayMemberPath? Its just a shortcut for an ItemTemplate containing a TextBlock showing the value in DisplayMemberPath. With your own ItemTemplate you have much more flexibility what and how you want to display.
Just add a TextBlock into your ItemTemplate and set Text="{Binding Value}"
and you have what you want.
As described here
This property is a simple way to define a default template that
describes how to display the data objects.
DisplayMemberPath provides a simple way to a Template, but you want a different one. You can't and you don't need both.
I still can't figure out how to get it to draw using the DisplayMemberPath
, however I did find the piece I was missing to get it drawing using the ItemTemplate
- I needed the ContentTemplate
binding
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}"
ContentTemplate="{TemplateBinding ContentPresenter.ContentTemplate}"
IsChecked="{Binding Path=IsSelected,RelativeSource={
RelativeSource TemplatedParent},Mode=TwoWay}" />
Then in my XAML I can write:
<ListBox Style="{StaticResource RadioButtonListBoxStyle}"
ItemsSource="{Binding MyCollection}"
SelectedValuePath="Key">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks to dowhilefor for pointing out that DisplayMemberPath
is simply a shortcut way of writing the ItemTemplate
, and that both cannot be set at once.
I have been researching this same problem since morning and this thread helped me a lot. I investigated the solution and found out there is a way to support DisplayMemberPath even after defining a custom ListboxItem controltemplate in a style . The key is adding
ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"
to the contentpresenter( the radiobutton in case of this thread).
The reason this will work is because internally the displaymemberpath property causes the Listbox to assign the ContentTemplateSelector to a "DisplayMemberTemplateSelector" template selector. Without this TemplateBinding in place, this selector does not take effect.
Cheers!
tl;dr
Set an ItemContainerStyle with a ContentPresenter in it, and make sure not to overwrite the ItemTemplate.
The need
You want to define a generic style to be re-used.
You want to re-use it with different data types, so you want to be able to use DisplayMemberPath or ItemTemplate when re-using - without having to redefine the whole style.
The problem
You were not using a ContentPresenter on your item's container.
The item itself would have nowhere to be drawn.
The solution
For your concrete case
Put a ContentPresenter inside the RadioButton; that should work:
<RadioButton IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
VerticalAlignment="Center">
<ContentPresenter />
</RadioButton>
In general
Define the ItemContainerStyle. This lets you control how each item is wrapped.
That Style targets a ListBoxItem.
Define that style's Template, making sure to include a ContentPresenter (where the content of the item itself will be shown).
A sample
Defining the style
<Style TargetType="{x:Type ListBoxItem}" x:Key="BigListBoxItemStyle" BasedOn="{StaticResource DefaultListBoxItemStyle}">
<Setter Property="Foreground" Value="DeepPink" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="71" />
<Setter Property="FontSize" Value="18" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="10 5 10 5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Background="{TemplateBinding Background}"
Margin="{TemplateBinding Margin}"
BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Center" />
<Rectangle x:Name="GraySeparator"
Grid.Row="1"
Height="1" Stroke="Gray" Opacity="0.2"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="Background" Value="Yellow" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="BorderBrush" Value="DarkGreen" />
<Setter Property="Visibility" Value="Hidden" TargetName="GraySeparator" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BigListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle" Value="{StaticResource BigListBoxItemStyle}" />
</Style>
Using it
<ListBox Style="{StaticResource BigListBoxStyle}"
ItemsSource="{Binding MyTuples}"
DisplayMemberPath="Item2"
SelectedItem="{Binding SelectedTuple}">