In XAML, RadioButton is like CheckBox (they both inherit from ToggleButton) with 2 differences: 1) they look different, and 2) when grouped together, checking one RadioButton will uncheck the others. But how should a developer implement the RadioButton? It is a little confusing, especially when data binding. I will demonstrate three approaches.
Aside: I will also point you to this article which discusses the problems of a three-state CheckBox and how binding to a nullable type in WinRT-XAML can create a lot of developer consternation. I realize that article is about CheckBox and this article is about RadioButton, but it’s tangentially relevant.
The Checked Event approach
Most developers start here. I simply hate this approach. I think it does almost everything in the face of good design, but sometimes getting software out the door is the most important priority – and this might just get you there. In light of that abstract justification, I’ll walk you through it.
In the code above, we setup three radio buttons. It is their container that determines if they are vertical or horizontal; multiple RadioButtons do not lay themselves out in any way. The RadioButton does show it’s content (which is typically text but because it inherits from ContentControl, the value of Content can be any type of XAML tree). Our code renders like this:
But we aren’t ready yet. This incomplete implementation allows more than one RadioButton to be checked (see how we use Checked like CheckBox? Remember a RadioButton and CheckBox are almost completely identical).
To solve this we use the RadioButton-specific property GroupName. Every RadioButton which is contained in the logic of “only one” gets the same GroupName, and the control framework will ensure only one is ever checked.
Aside: the GroupName property is the one and only property specific to the RadioButton (compared to a CheckBox). Like CheckBox, RadioButton inherits the IsThreeState property from its base, ToggleButton. I think it’s reasonable to say that IsThreeState is irrelevant in 99% of RadioButton use cases.
With GroupName set, every RadioButton in scope, regardless of its location, will allow only one RadioButton in its group to be checked. That saves the developer from writing a lot of redundant boilerplate logic.
Because there’s no Value property to a RadioButton, translating the value of Content to an numeric value you might use in an Enumeration is only possible two ways: 1) creating a custom Attached Property, and 2) leveraging the Tag property all controls have (inherited from FrameworkElement). In this case, I’ll use Tag, but either approach will have the same result.
In the code above I set the value of the RadioButton using the Tag property. Since the data type of Tag is object, this isn’t strongly-typed. This means that when you read it, you will need to cast it. It also means it may not cast – so just know you should code defensively, like normal.
In the code above, I add the Checked event handler called Radio_Checked which will be implemented in my code-behind. If you are an MVVM developer you might have just gotten the chills. But this is how it’s done. If you want a pure data binding solution, just wait. I’m getting there.
Here’s what the implementation will look like:
In the code above, I’m implementing the Checked event handler called Radio_Checked. This handler handles the Checked event for all the RadioButtons, differentiating them based on the value of their Tag property. In this case, the Tag has the integer value that I want to set in the property Value – a custom property I added to my code-behind class. How will I use the Value property? That’s going to be up to your own application logic. It doesn’t matter.
Are we done?
We really are. The GroupName will ensure that only one RadioButton is checked, and my Checked event handler will update the Value property to the currently selected value. Is that elegant? No. Not at all. It’s a filthy mess that hints at all types of problems. Plus, there’s no binding so MVVM isn’t enabled. Having said all that, that was the textbook implementation of RadioButton in XAML.
Now, let’s see how we would really use RadioButton.
The IValueConverter approach
I think most developers do it this way. I’m torn if this is better than the third approach, multiple properties. I think they both have their merits, but if this one has any down-side its that it introduces so many converters onto the page. It’s no secret that converters are one of the most significant killers of performance (second perhaps to binding itself) in a XAML application.
That being said, let’s see how a converter can enable binding.
The most important part of this approach is going to be the converter itself. It will implement the IValueConverter interface that all value converters implement. But, unlike most converters, it will be important that we implement both the Convert and the ConvertBack methods. For the sake of awesome naming-conventions, let’s call it the RadioConverter.
In the code above, we create the RadioConverter which will accept a value from the ViewModel, compare it to the converter parameter, and return a boolean. Similarly, the ConvertBack method will accept a boolean (which indicates the Checked state of the RadioButton) and return the parameter when true.
This part is important.
Because this converter will be called when a RadioButton is checked and unchecked, we want to invalidate the operation when the checked state is false. We do this by returning null. This only works if the property in the ViewModel is not nullable (like int). Since the framework can’t set a non-nullable value to null, it aborts the operation. If this were not how it worked, the ViewModel property will be set two times, once for the new checked value, then again to the previous (unchecked) value. The result would be no change to the value.
Look: if you must have a nullable data type for your ViewModel property, you’re on your own. The code to make nullables work with this approach is too laborious. If your type is nullable, then just skip to the next approach, which will with nullable types work fine.
Now we can bind to the IsChecked property, a present from RadioButton’s parent, the ToggleButton. We haven’t talked about this property before. But this is the property to which we will bind – and we will bind with (Mode=TwoWay) because we want the user’s interaction with the RadioButton to update the underlying ViewModel. Right? Right.
Here’s the XAML syntax:
In the code above, I am only showing button “one”. Clearly, you would repeat this syntax for each RadioButton. Notice how there is no Tag property set. Notice how there is no Checked event handler. Only the Content and the IsChecked properties are set (of course, the Content property is not necessary to implement this solution, but it’s pretty difficult to use without it).
The binding syntax sets four things: 1) it binds to the Value path. This is the name of the property in my ViewModel. Naturally, this would be different for you, 2) Converter is set to my RadioConverter, which is defined in the Page.Resources higher in the logical tree, 3) ConverterParameter is set to 1, which is what prompts the logic in RadioConverter and is why we no longer need the Tag property, and 4) Mode is set to TwoWay. Mode is important if you want the interaction of your user to update the underlying value in your ViewModel. Leave Mode set to OneTime or OneWay and you won’t be happy with the results – and neither will your user.
In the code above, we can see how the property is defined in the ViewModel. It’s a typical property. The important takeaway here is that it is not nullable. As stated earlier, this solution does not support nullable types.
What’s the downside? Binding is an upside and a downside. Converters are only a downside when you use a lot of them. If you think that the number of converters you have might be negatively impacting your application, then you might look into the next approach which does not use converters..
The Expanded-property approach
Code has bugs. More code, more bugs. I know that. But software doesn’t run without code. Sometimes we have to actually write it. This expands the possible values and creates a property for each. In our case, since we know there is a 1, 2, and 3 RadioButton, we will create ValueAs1, ValueAs2, and ValueAs3 properties to which each RadioButton can bind to individually.
In the code above, we expand the Value property with companion properties for each possible value. They each return boolean, indicating if the Value property equals the value for which the property was created. They also are writeable; their setter updated Value which, in turn, raises PropertyChanged for each of these companion properties, including Value itself.
If you use this approach, your XAML would look like this:
In the code above, you can easily see how simplified the XAML becomes. That’s because the bulk of the complexity has been moved into the ViewModel – where it belongs. You might be thinking that this complicates the ViewModel. It certainly introduces new complexity in the ViewModel, but it also removes complexity from the XAML and the runtime domain. Additional complexity isn’t always a problem. In this case, I think it’s very reasonable.
Even though I understand what makes you squirm over ViewModel complexity, remember the ViewModel is fully compiled at runtime and executes many-fold more efficiently than a runtime-invoked converter (which must be invoked both ways and for newly-checked and recently-unchecked RadioButton).
I would argue that this approach is not any more complex than a converter trying to interpret the IsChecked value. The complexity is negligible compared to the performance benefit you get from inverting where the complexity lies.
Conclusion
That RadioButton is more complex that it looks, huh? Using it right out-of-the-box certainly gets the job done, but it doesn’t enable complex design patterns. But what’s the right way to use it. I’ve shown you two great ways to enable data binding with a RadioButton. But, you should know, there are more.
Putting a RadioButton inside an ItemsControl is be a way to create a RadioButtonList – a control that doesn’t exist in the Toolbox. This would be important if the number of possible options is variable. Also, extending the RadioButton by sub-classing it, we could create the appropriate properties to enable binding without requiring a converter or changes to our ViewModel.
Aside: if you decide to sub-class the RadioButton, I think that’s fine. But, to save you a few iterations, let me point you toward creating Dependency Properties and not CLR properties – since Dependency Properties are what you need to handle external binding scenarios.
What’s the best way? What should you do for your project? Well, it depends. In the meanwhile, I hope this article enables 90% of the apps out there looking to simply bind to a RadioButton. The flexibility of XAML is awesome, but it’s also exhausting when we consider how many ways we can solve a single problem. If you have your own solution, feel free to share it in the comments.
When I bind to a RadioButton, I use the expanded-property approach.
Best of luck!