During the process of writing a WPF application recently, I had the need for a data bound list of items where the options had to be mutually exclusive, so I figured something like a list of RadioButtons would be in order.
However, when I started looking around I could not find a RadioButtonList or anything that fitted the bill out-of-the-box. Therefore, I thought I’d put something together myself; I also needed to have the list render horizontally rather than vertically. This is what I came up with:
<!-- Item Style for the ListBoxItem to add a RadioButton -->
<Style x:Key="RadioButtonItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="0,0,5,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderThickness="0" Background="Transparent">
<!-- Note: IsChecked is bound to IsSelected-->
<RadioButton
Focusable="False"
IsHitTestVisible="False"
IsChecked="{TemplateBinding IsSelected}">
<ContentPresenter />
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Turns the ListBox in to a Horizontal ListBox -->
<ItemsPanelTemplate x:Key="HorizontalItemsPanel">
<VirtualizingStackPanel
Orientation="Horizontal" />
</ItemsPanelTemplate>
I defined a Style and the ItemsPanelTemplate in the Resources property of my Window that contains all the necessary XAML for the effects I need; note that you could just as easily define this XAML inline on the ListBox as opposed to using the Resources property. I then applied these two new elements to an instance of ListBox using the following mark-up:
<ListBox
BorderThickness="0"
ItemsSource="{Binding MyDataList}"
SelectedValue="{Binding MyDataListSelectedValue}"
ItemContainerStyle="{StaticResource RadioButtonItemStyle}"
ItemsPanel="{StaticResource HorizontalItemsPanel}" />
I’ve highlighted where I’ve used the two resources.
For this example the data list is trivial and is provided by a ViewModel class attached to the DataContext of the Window; I’m only showing the code here as a example of how you can add sample data to an application, as well as proving that the above XAML all works as expected using a bound list of data:
public class MainWindowViewModel
{
public IEnumerable<string> MyDataList
{
get
{
yield return "Stan";
yield return "Cartman";
yield return "Kenny";
yield return "Karl";
}
}
public string MyDataListSelectedValue
{
get { return "Cartman"; }
set { /* TODO: save the value */ }
}
}
<!-- XAML -->
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
Here’s what it all looks like in the Visual Studio designer:
And there we have it, job done. Hope this helps someone, happy XAML hacking.