Introduction
This is the WPF version of my previous articles: CRadioListBox: A ListBox with Radio Buttons (MFC Version) and RadioListBox: A ListBox with Radio Buttons (WinForms Version). As explained in those articles, RadioListBox
has the same functionalities as a regular ListBox
, but with a different look and feel. Anyway, the RadioListBox
control offers some advantages:
- It is clearer that options are mutually exclusive with radio buttons.
- It is a good alternative to a group of radio buttons because you have to maintain just one control, less memory use.
- It inherits some useful features like scrolling, sorting, data binding, and multi-column.
- It will be easier to change options dynamically, as shown in the demo application.
- It will be easier to manage selection events, also shown in the demo application.
Using the Code
To implement RadioListBox
into your project, you just need to do a few steps:
- Include RadioListBox.xaml and RadioListBox.xaml.cs into your project.
- Drop a
RadioListBox
object into your XAML form, or do it by hand. An alias for the System.Windows.Controls.Custom
namespace will be useful.
- Change the standard properties of the control, just like for a
ListBox
.
- Use the
IsTransparent
property to mimic a group of RadioButton
s.
That's all! Now you can use the radio button collection as a regular ListBox
. You can add items with the Items.Add()
method, the ItemsSource
property, or directly into XAML. Finally, query for user selection with the SelectedIndex
property. No more one-by-one evaluations.
RadioListBox Internals
The RadioListBox
class is derived from WPF's ListBox
class with a custom ControlTemplate
to remove all undesired ListBox functionalities like selected item's background. To be consistent with the radio button model, multiple selection has been disabled. The resumed XAML implementation is the following:
<ListBox x:Class="System.Windows.Controls.Custom.RadioListBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib" >
<ListBox.Resources>
<Style x:Key="{x:Type ListBoxItem}" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<RadioButton x:Name="radio" Click="ItemRadioClick">
<RadioButton.Content>
<ContentPresenter ... />
</RadioButton.Content>
</RadioButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.Template>
<ControlTemplate>
<Border BorderThickness="0"
Padding="1,1,1,1"
Background="Transparent"
Name="theBorder"
SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}"
Focusable="False">
<ItemsPresenter
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers ... />
</ControlTemplate>
</ListBox.Template>
</ListBox>
As the radio buttons inside the ControlTemplate
don't change the selection, this has to be done manually inside the ItemRadioClick
event. Here, the ItemContainerGenerator
property has an important role by converting between the ListBox
's items contents and the ListBoxItem
presentation object. Here is a summarized C# source code:
namespace System.Windows.Controls.Custom
{
public partial class RadioListBox : ListBox
{
public RadioListBox() { ... }
public new SelectionMode SelectionMode { ... }
public bool IsTransparent { ... }
private void ItemRadioClick(object sender, RoutedEventArgs e)
{
ListBoxItem sel = (e.Source as RadioButton).TemplatedParent as ListBoxItem;
int newIndex = this.ItemContainerGenerator.IndexFromContainer(sel); ;
this.SelectedIndex = newIndex;
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
CheckRadioButtons(e.RemovedItems, false);
CheckRadioButtons(e.AddedItems, true);
}
private void CheckRadioButtons(System.Collections.IList radioButtons, bool isChecked)
{
foreach (object item in radioButtons)
{
ListBoxItem lbi =
this.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
if (lbi != null)
{
RadioButton radio = lbi.Template.FindName("radio", lbi) as RadioButton;
if (radio != null)
radio.IsChecked = isChecked;
}
}
}
}
}
History
- September 6th, 2009: First version.