Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Selected RadioButton Control

0.00/5 (No votes)
29 Aug 2017 1  
There are times when want to have associated RadioButton controls on a window, and it would be much easier to interface to these controls with a single property instead of independent properties for each RadioButton. This control allows multiple RadioButton controls to be on a single Binding.

Introduction

I recently had to create a window with a images of which one could be selected, but the items were in different groups. The RadioButton would work great for this requirement. However, my idea of using a ListBox with the DataTemplate of a RadioButton would not be so straight forward. I had wanted to use a ListBox because that way would not have to have all the items specified in both the ViewModel and the View.  In addition, there was an image associated with each one that was a resource. To specify the image to be displayed I would require some way of specifying which image.

I started thinking and decided it would be better to put all the information required for displaying each item in the XAML and not try to depend on the ViewModel for any of this information, and then just have a way to pass the needed information from the selected RadioButton to the ViewModel using Binding. This is when I came up with creating a Control to contain the part of the display that contained XAML which included all the RadioButton controls. In addition, I added a GroupName DependencyProperty to allow multiple groups of RadioButton controls to be contained with this XAML, the ones that will be associated with this new ContentControl determined by the GroupName. If a GroupName is not provided, then all RadioButton controls within the XAML would be associated with this ContentControl.

The Design

This Control is derived from the ContentControl:

public class SelectedRadioButtonControl : ContentControl
{
    public SelectedRadioButtonControl()
    {
        Loaded += (sender, args) =>
        {
            var items = FindVisualChildren(this).ToList();
            if (!string.IsNullOrWhiteSpace(GroupName))
                items = items.Where(j => j.GroupName == GroupName).ToList();
            items.ToList().ForEach(i => i.Checked += (radioButton, arg)
                => SelectedItem = radioButton);
        };
    }

    /// <summary>
    /// SelectedItem DependencyProperty
    /// </summary>
    public object SelectedItem
    {
        get { return GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object),
            typeof(SelectedRadioButtonControl),
            new PropertyMetadata(null));

    /// <summary>
    /// GroupName DependencyProperty
    /// </summary>
    public string GroupName
    {
        get { return (string)GetValue(GroupNameProperty); }
        set { SetValue(GroupNameProperty, value); }
    }

    public static readonly DependencyProperty GroupNameProperty =
        DependencyProperty.Register("GroupName", typeof(string), typeof(SelectedRadioButtonControl),
            new PropertyMetadata(null));
            public IEnumerable<RadioButton> FindVisualChildren(DependencyObject depObj)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
            if (child != null && child is RadioButton)
                yield return (RadioButton)child;
            foreach (RadioButton childOfChild in FindVisualChildren(child))
                yield return childOfChild;
        }
    }
}

When this Control is Loaded the content of the SelectedRadioButtonControl is searched using the VisualTreeHelper to find all instances of RadioButton. If a GroupName is specified (meaning GroupName is not null nor empty), then this collection is filtered for all RadioButton controls that have that GroupName. For each of these RadioButton controls, its Checked event is subscribed to so that the control knows when one of its RadioButton controls is selected. Also, the IsChecked property is checked and if the value is true, this is known to be the currently selected RadioButton.

There is a DependencyPropery defined for SelectedRadioButton and GroupName. The SelectedRadioButton DependencyPropery is of Type RadioButton and is set to the last RadioButton that fire its Checked event. The second DependencyProperty, GroupName is of Type string and is used to filter the contained RadioButton controls to those that have a GroupName that matches this GroupName.

Using the Control

The following is an example of how to use this control:

<local:SelectedRadioButtonControl x:Name="Group1SelectedRadioButtonControl1"
                                  GroupName="Group1">
    <local:SelectedRadioButtonControl x:Name="Group1SelectedRadioButtonControl2"
                                      GroupName="Group2">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="3*" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel HorizontalAlignment="Center"
                        VerticalAlignment="Center">
                <RadioButton Content="Group 1 A"
                             GroupName="Group1" />
                <RadioButton Content="Group 1 B"
                             GroupName="Group1" />
                <RadioButton Content="Group 2 A"
                             GroupName="Group2" />
                <RadioButton Content="Group 1 C"
                             GroupName="Group1" />
                <RadioButton Content="Group 2 B"
                             GroupName="Group2" />
                <RadioButton Content="Group 2 C"
                             GroupName="Group2" />
            </StackPanel>
            <TextBlock Grid.Row="1"
                       Grid.Column="0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="{Binding ElementName=Group1SelectedRadioButtonControl1,
                                      Path=SelectedRadioButton.Content,
                                      StringFormat='Group 1 selected RadioButton: {0}'}" />
            <TextBlock Grid.Row="1"
                       Grid.Column="1"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="{Binding ElementName=Group1SelectedRadioButtonControl2,
                                      Path=SelectedRadioButton.Content,
                                      StringFormat='Group 2 selected RadioButton: {0}'}" />
        </Grid>
    </local:SelectedRadioButtonControl>
</local:SelectedRadioButtonControl>

In this sample there are two SelectedRadioButtonControl controls, one inside of the other, and each associated with a different GroupName. The six RadioButton controls are associated with one group or the other using its GroupName which matches the GroupName of one of the SelectedRadioButtonControl controls. A TextBlock Text property is bound to each of the SelectedRadioButtonControl SelectedRadioButton.Content property.

History

  • 08/23/17: Initial Version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here