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.

3 comments:
Any ideas when you start to see benefits to using a VirtualizingStackPanel? I'm guessing at such low numbers there isn't really any benefit?
You're absolutely right about the low numbers here; you could simply use a StackPanel. I would also argue that with something like a RadioButtonList you would never really have enough elements to warrant a virtualized panel.
However, as a best practice I would always attempt to keep the template as close to the original control template as possible. There are a number of reasons for this, not least of which is the "special" un-written contracts with some control templates, where the WPF developers have done some optimizations or provided additional behaviour if they find elements of a certain type or find element of a certain type and with a specific name e.g. PART_nnn (ScrollBar, ProgressBar and TextBox are all good example of this).
-PJ
Here's a post on the Forums from Dr. WPF on using a style to create a radio button list
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/5137aabc-bb3a-478a-9438-bc93dd9cc0ac/
Post a Comment