Binding IsChecked property of RadioButton in WPF
If you have tried to bind the RadioButton’s IsChecked property in WPF to an object, you have most likely experienced the following problem: In OneWay bindings it works great. But if you have more than one RadioButtons binded TwoWay and you click on an unchecked one, you were expecting that the object to which the previously checked RadioButton was binded to receive the value of False. But you were wrong in your expectations. That’s because for some reasons Microsoft does not obey bindings and does not pass the False value to the DependencyProperty and instead of that they just assign the value False directly to the property, which ruins the binding.
There are many proposed solutions to this around the internet, problem with all those is that they do not work with dynamically generated controls. So since I had to find a way to make this working with dynamic controls, decided to make a wrapper of the real RadioButton which will correctly Bind in two ways. Here is the code for the wrapper:
using System;
using System.IO;
using System.Printing;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Microsoft.Win32;
namespace Controls
{
public class RadioButtonExtended : RadioButton
{
static bool m_bIsChanging = false;
public RadioButtonExtended()
{
this.Checked += new RoutedEventHandler(RadioButtonExtended_Checked);
this.Unchecked += new RoutedEventHandler(RadioButtonExtended_Unchecked);
}
void RadioButtonExtended_Unchecked(object sender, RoutedEventArgs e)
{
if (!m_bIsChanging)
this.IsCheckedReal = false;
}
void RadioButtonExtended_Checked(object sender, RoutedEventArgs e)
{
if (!m_bIsChanging)
this.IsCheckedReal = true;
}
public bool? IsCheckedReal
{
get { return (bool?)GetValue(IsCheckedRealProperty); }
set
{
SetValue(IsCheckedRealProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsCheckedReal. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedRealProperty =
DependencyProperty.Register("IsCheckedReal", typeof(bool?), typeof(RadioButtonExtended),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
IsCheckedRealChanged));
public static void IsCheckedRealChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
m_bIsChanging = true;
((RadioButtonExtended)d).IsChecked = (bool)e.NewValue;
m_bIsChanging = false;
}
}
}
So now all you have to do is to use the ExtendedRadioButton instead of the built-in one and bind to the IsCheckedReal property instead of the IsChecked one.
Enjoy 🙂
It works great! Thanks, I like it because it is clean and simple (and works as expected)!
Remember, this approach will not work when used with styles and triggers, see: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/8eb8280a-19c4-4502-8260-f74633a9e2f2
Also: To get the default BindingMode (and equivalant DependencyProperty) behavior as the regular WPF RadioButton, change the DependencyProperty to:
public static readonly DependencyProperty IsCheckedRealProperty =
DependencyProperty.Register(“IsCheckedReal”, typeof(bool?), typeof(ToggleButton),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
IsCheckedRealChanged));
Thanks for the suggestion! Changed it in the code above.
As for the styles and triggers – you may be right and it may not work as expected.
Totally AWESOME! And I learned some good stuff. Thank you so much! You saved me hours!!!
Why is m_bIsChanging static??
Well, m_bIsChanging is static because it is used in a static method. You can change to non static and create a public property that will return m_bIsChanging. After that in the static event you should use
((RadioButtonExtended)d).IsChanging = true;
and
((RadioButtonExtended)d).IsChanging = false;
Although I wasted half of my day but still finally good to see a working solution.
Thanks a lot Peter 🙂
Well done, this works great.
Thank you.
Thank you very much! I took me hours of frustration wondering why this does not out of the box!
Wow. I spent so much time fighting with this to finally discover it was a bug, then to finally discover a workaround that actually works. Nice job, I am using your class definition to successfully work around the issue for what I was trying to do.
What a resolution… Amazing.
Thanks a ton. You saved my day.
All others failed for me
Maybe a little late, but why do you declare a bool?, and use a bool as property definition?
This was a mistake. Thanks for the heads up and it is changed now in the original post.
Thanks dude! Saved a lot of time 🙂
awesome, thanks man!
small comment about someone saying you can't apply styles, triggers..
you totally can!
just target correct namespaces for that. In the styles example:
lets say you pull
xmlns:common="clr-namespace:…RadioButtonExtended folder"
In the style TargetType="common:RadioButtonExtended"…Sample Setter Property="RadioButton.Margin" Value="8"
as long as you instanciate the RadioButtonExtended with the same namespace:
common:adioButtonExtended.. you good to go
Anyway, thanks for your post
After trying many other solutions that didn't work. I tried this. Same problem every where. Binding/Convert works once on Load. After that, checking the radio buttons never seems to fire the Binding again. The ConvertBack method in my Binding is never fired.
@ristogod
Did you set your binding to the new IsCheckedReal property or the IsChecked one? Basically it should not matter if there is a converter or not as the converting is fired automatically by the framework itself. Also take a look at the first comment where there is a link that this workaround will not work with styles and triggers.
very very nice tnx
Have later versions of .NET fixed the bug this was working around?
Great job. Thank you for your effort.
worked for me. thanks!
very great
If I set the IsCheckedReal via databinding during runtime, its unchecks the other radiobuttons correct, but doesn't set the radiobutton as checked. On initialze it works.
Any suggestions? Thanks in advance.
Peter
I'm not sure understand your explanation, but make sure you have bound all the radio buttons to the IsCheckedReal property and not the original RadioButton.IsChecked property.
Hello Peter
Thanks for the reply. All of my radio buttons are only bound to IsCheckedReal.
To clarify: If I set the property on the ViewModel (which is bound with IsCheckedReal) during runtime this button is not visually set (but the internal state is correct)
I seems to be that the GUI is not updated. Or in other words: if I set the button in the viewmodel during runtime, all buttons are unchecked and none of them is checked.
In operation with the mouse it works correct.
Thanks again,
Peter
In this case make sure your ViewModel implements correctly the INotifyPropertyChanged (http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx) interface. It is required when you want your model changes to be reflected on the UI (not specifically for the Radiobuttons but for everything that you have bound). Hope this helps.
Hello Peter
Thanks for the answer. INotifyPropertyChanged works correct. The changes are comming to the radiobutton. But the radiobutton GUI works not correct if the button is set programmatically. (other buttons will be cleard but none is set)
Anyway, Thanks for your help.
Peter
I have no idea why it does not for you. I have created a simple project locally and setting the boolean values in the model correctly updates the UI.
Hello Peter
Thanks a lot for your help and patience. Now I have made also a small project and there the radiobutton works perfekt. There must be something in my project that disturbs it.
Peter
Hello Peter
Now I found the reason for the strange behaviour. I have used GroupName in the radiobuttons. Without it works. My solution is now to overwrite OnChecked and OnUnchecked (and do nothing) in the rb and do the logic in the viewmodel.
Thanks again, Peter
Working well, thank you very much
Hi Peter,
Thanks for your solution. I implemented same in my code and it worked fine for the scenario where normal radio button was causing trouble.
However on testing further I found few issue in the implementation.
If we will set value from ViewModel then it sets value perfectly fine but doesn't revert the value of previously selected radio from binded variable. It only reflects on UI.
so I have come up with some minor changes which takes care of all the problem.
public class RadioButtonExtended : RadioButton
{
static bool m_bIsChanging = false;
public RadioButtonExtended()
{
this.Checked += new RoutedEventHandler(RadioButtonExtended_Checked);
this.Unchecked += new RoutedEventHandler(RadioButtonExtended_Unchecked);
}
void RadioButtonExtended_Unchecked(object sender, RoutedEventArgs e)
{
this.IsChecked = false;
}
void RadioButtonExtended_Checked(object sender, RoutedEventArgs e)
{
this.IsChecked = true;
}
public new bool? IsChecked
{
get { return (bool?)GetValue(IsCheckedRealProperty); }
set
{
SetValue(IsCheckedRealProperty, value);
SetValue(IsCheckedProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsCheckedReal. This enables animation, styling, binding, etc…
public static readonly DependencyProperty IsCheckedRealProperty =
DependencyProperty.Register("IsChecked", typeof(bool?), typeof(RadioButtonExtended),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
IsCheckedRealChanged));
public static void IsCheckedRealChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (((RadioButtonExtended)d).IsChecked == (bool)e.NewValue)
{
((RadioButtonExtended)d).IsChecked = (bool)e.NewValue;
}
}
}
Thank you. It helps me much. Awesome… 😀