EDIT: Problem was fixed in .NET 4.0.
I have been trying to bind a group of radio buttons to a view model using the IsChecked
button. After reviewing other posts, it appears that the IsChecked
property simply doesn't work. I have put together a short demo that reproduces the problem, which I have included below.
Here is my question: Is there a straightforward and reliable way to bind radio buttons using MVVM? Thanks.
Additional information: The IsChecked
property doesn't work for two reasons:
When a button is selected, the IsChecked properties of other buttons in the group don't get set to false.
When a button is selected, its own IsChecked property does not get set after the first time the button is selected. I am guessing that the binding is getting trashed by WPF on the first click.
Demo project: Here is the code and markup for a simple demo that reproduces the problem. Create a WPF project and replace the markup in Window1.xaml with the following:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<StackPanel>
<RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" />
<RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" />
</StackPanel>
</Window>
Replace the code in Window1.xaml.cs with the following code (a hack), which sets the view model:
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Window1ViewModel();
}
}
}
Now add the following code to the project as Window1ViewModel.cs
:
using System.Windows;
namespace WpfApplication1
{
public class Window1ViewModel
{
private bool p_ButtonAIsChecked;
/// <summary>
/// Summary
/// </summary>
public bool ButtonAIsChecked
{
get { return p_ButtonAIsChecked; }
set
{
p_ButtonAIsChecked = value;
MessageBox.Show(string.Format("Button A is checked: {0}", value));
}
}
private bool p_ButtonBIsChecked;
/// <summary>
/// Summary
/// </summary>
public bool ButtonBIsChecked
{
get { return p_ButtonBIsChecked; }
set
{
p_ButtonBIsChecked = value;
MessageBox.Show(string.Format("Button B is checked: {0}", value));
}
}
}
}
To reproduce the problem, run the app and click Button A. A message box will appear, saying that Button A's IsChecked
property has been set to true. Now select Button B. Another message box will appear, saying that Button B's IsChecked
property has been set to true, but there is no message box indicating that Button A's IsChecked
property has been set to false--the property hasn't been changed.
Now click Button A again. The button will be selected in the window, but no message box will appear--the IsChecked
property has not been changed. Finally, click on Button B again--same result. The IsChecked
property is not updated at all for either button after the button is first clicked.
A small extension to John Bowen's answer: It doesn't work when the values don't implement
ToString()
. What you need instead of setting theContent
of the RadioButton to a TemplateBinding, just put aContentPresenter
in it, like this:This way you can additionally use
DisplayMemberPath
or anItemTemplate
as appropriate. The RadioButton just "wraps" the items, providing the selection.Not sure about any IsChecked bugs, one possible refactor you could make to your viewmodel:the view has a number of mutually exclusive states represented by a series of RadioButtons, only one of which at any given time can be selected. In the view model, just have 1 property (e.g. an enum) which represents the possible states: stateA, stateB, etc That way you wouldn't need all the individual ButtonAIsChecked, etc
I have a very similar problem in VS2015 and .NET 4.5.1
XAML:
....
As you can see in this code i have a listview, and items in template are radiobuttons that belongs to a groupname.
If I add a new item to the collection with the property Selected set to True it appears checked and the rest of buttons remain checked.
I solve it by getting the checkedbutton first and set it to false manually but this is not the way it's supposed to be done.
code behind:
`
I know this is an old question and the original issue was resolved in .NET 4. and in all honesty this is slightly off topic.
In most cases where I've wanted to use RadioButtons in MVVM it's to select between elements of an enum, this requires binding a bool property in the VM space to each button and using them to set an overall enum property that reflects the actual selection, this gets very tedious very quick. So I came up with a solution that is re-usable and very easy to implement, and does not require ValueConverters.
The View is pretty much the same, but once you have your enum in place the VM side can be done with a single property.
MainWindowVM
The class that does all the work inherits from DynamicObject. Viewed from the outside it creates a bool property for each element in the enum prefixed with 'Is', 'IsRed', 'IsBlue' etc. that can be bound to from XAML. Along with a Value property that holds the actual enum value.
EnumSelectorVM
To respond to changes you can either subscribe to the NotifyPropertyChanged event or pass an anonymous method to the constructor as done above.
And finally the MainWindow.xaml
Hope someone else finds this useful, 'cause I reckon this ones going in my toolbox.
If you start with Jason's suggestion then the problem becomes a single bound selection from a list which translates very nicely to a
ListBox
. At that point it's trivial to apply styling to aListBox
control so that it shows up as aRadioButton
list.