Click here to Skip to main content
13,145,068 members (54,903 online)
Click here to Skip to main content
Add your own
alternative version

Stats

1.9K views
20 downloads
5 bookmarked
Posted 2 Sep 2017

WPF RadioButton Switched Content Control

, 2 Sep 2017
Rate this:
Please Sign up or sign in to vote.
Presents a very simple control with an IsChecked property that allows definition of two different contents depending of if IsChecked property value is true or false

Introduction

I had a requirement for a functionality where there would display read-only information until it was selected, when it would then display controls that would allow editing, and this Control was in an ItemsControl. Only one of these would be open at a time for editing. There was originally a lot of code in the ViewModel to enable this functionality. I really did not like this and thought that this was the perfect application for a custom RadioButton.

The Design

There are two parts for this control: the C# class that is derived from RadioButton and the XAML that provides layout information. The C# class is as follows:

[TemplatePart(Name = ToggleOnContentName, Type = typeof(ContentControl))]

[TemplatePart(Name = ToggleOffContentName, Type = typeof(ContentControl))]
public class ContentChangeRadioButton : RadioButton
{
    private const string ToggleOnContentName = "Part_ToggleOnContent";
    private ContentPresenter _ToggleOnContent;
    private const string ToggleOffContentName = "Part_ToggleOffContent";
    private ContentPresenter _ToggleOffContent;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _ToggleOnContent = (ContentPresenter)GetTemplateChild(ToggleOnContentName);
        _ToggleOffContent = (ContentPresenter)GetTemplateChild(ToggleOffContentName);
        _ToggleOnContent.Visibility = (IsChecked == true)
    ? Visibility.Visible : Visibility.Collapsed;
        _ToggleOffContent.Visibility = (IsChecked == true)
    ? Visibility.Collapsed : Visibility.Visible;
        Checked += ContentChangeRadioButton_Checked;
        Unchecked += ContentChangeRadioButton_Unchecked;
    }

    private void ContentChangeRadioButton_Unchecked(object sender, RoutedEventArgs e)
    {
        if (IsChecked == false) //Without this when opened combobox in content would uncheck button
        {
            _ToggleOnContent.Visibility = Visibility.Collapsed;
            _ToggleOffContent.Visibility = Visibility.Visible;
        }
    }

    private void ContentChangeRadioButton_Checked(object sender, RoutedEventArgs e)
    {
        _ToggleOnContent.Visibility = Visibility.Visible;
        _ToggleOffContent.Visibility = Visibility.Collapsed;
    }

    static ContentChangeRadioButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentChangeRadioButton),
            new FrameworkPropertyMetadata(typeof(ContentChangeRadioButton)));
    }

    public object ToggleOnContent
    {
        get { return (object)GetValue(ToggleOnContentProperty); }
        set { SetValue(ToggleOnContentProperty, value); }
    }

    public static readonly DependencyProperty ToggleOnContentProperty =
        DependencyProperty.Register("ToggleOnContent", typeof(object),
            typeof(ContentChangeRadioButton),
            new PropertyMetadata(null));

    public bool ToggleState
    {
        get { return (bool)GetValue(ToggleStateProperty); }
        set { SetValue(ToggleStateProperty, value); }
    }

    public static readonly DependencyProperty ToggleStateProperty =
        DependencyProperty.Register("ToggleState", typeof(bool),
            typeof(ContentChangeRadioButton),
            new PropertyMetadata(false));

    private static void ToggleStateChanged
    (DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = (ContentChangeRadioButton)d;
        if ((bool)e.NewValue)
        {
            control._ToggleOnContent.Visibility = Visibility.Visible;
            control._ToggleOffContent.Visibility = Visibility.Collapsed;
        }
        else
        {
            control._ToggleOnContent.Visibility = Visibility.Collapsed;
            control._ToggleOffContent.Visibility = Visibility.Visible;
        }
    }
}

There is one important DependencyProperty, the ToggleOnContent. The direct content of the RadioButton is used for when the IsChecked property is false, and then the content specified when the IsChecked property is true is specified in this DependencyProperty.

The OnApplyTemplate method uses the GetTemplateChild method to get references to the ContentPresenter for both the unchecked and checked states, and also hooks up to the Checked and Unchecked events, and ensures that the Visibility of the controls for the checked and unchecked states are correct for the current IsChecked value.

The Checked and Unchecked event handlers set the Visibility of the two ContentPresenter controls to the value appropriate for the IsChecked value. You will notice that there is an additional check when the Unchecked event is fired to ensure that the control is truely in the IsChecked == false. I had some issue with this when I had some content that included a ComboBox, and this event would be fired when the ComboBox dropdown closed.

The XAML is actually very simple:

<Style TargetType="{x:Type local:ContentChangeRadioButton}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ContentChangeRadioButton}">
                <Grid>
                    <ContentPresenter x:Name="Part_ToggleOffContent"
                  Content="{TemplateBinding Content}"/>
                    <ContentPresenter x:Name="Part_ToggleOnContent"
                  Content="{TemplateBinding ToggleOnContent}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The ControlTemplate only has two ContentPresenter controls contained within a Grid control: there is a ContentPesenter for the IsChecked property being true and false.

Using the Control

To use this control, it simply has to be declared, and content specified for the ToggleOn state (which is declared with the ToggleOnContent attribute, and ToggleOff which is declared in the direct content:

<local:ContentChangeRadioButton Margin="2"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center">
    <Border Width="75"
            Height="25"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            BorderBrush="Black"
            BorderThickness="1">
        <TextBlock HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   Text="Toggle Off" />
    </Border>
    <local:ContentChangeRadioButton.ToggleOnContent>
        <Border Width="100"
                Height="30"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                BorderBrush="Blue"
                BorderThickness="1">
            <TextBlock HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="Toggle On" />
        </Border>
    </local:ContentChangeRadioButton.ToggleOnContent>
</local:ContentChangeRadioButton>

The single instance with ToggleOn state is automatically accomplished because this control is derived from the RadioButton.

The Sample

The sample has three of these controls, each one having the same content, one for on and one for off. The Content is just different text inside a different Border.

You will notice that when one of these controls is clicked, it changes and if there is a control that had previously been selected, it will change to the Toggle Off state. Looking at the code, you will see that there is no code that is causing this change since the base Control is of Type RadioButton.

Conclusion

This was a very simple control to create, and I think it is a nice little control that makes a lot of sense. It is basically a simplified Expander, but an Expander where the Header disappears. The simplicity is what is nice about it. To a certain extent, I am not happy with how complex some of the Microsoft controls are since you need to do so much work to change them, and simpler controls can be more easily modified to look exactly like you want them to, and creating more sophisticated designs could easily be either done directly or styles can be created. This is a very simple control.

History

  • 09/02/2017: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Clifford Nelson
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.170915.1 | Last Updated 2 Sep 2017
Article Copyright 2017 by Clifford Nelson
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid