How to Solve RadioButtons Binding issue for MVVM

No.of Views3799
Bookmarked0 times
Downloads 
Votes0
By  abhi2434   On  24 Mar 2011 08:03:59
Tag : WPF , Miscellaneous
Most of us when dealing with WPF applications must have been using MVVM pattern where you want to completely separate the presentation layer into a View Models. Well, it would be hard to create MVP or MVVM pattern yourself in other applications, but WPF has inbuilt support of MVVM with Command interfaces and Binding. Binding is the concept which lets you to update the control whenever the underlying data object is modified and vice versa.
emailbookmarkadd commentsprint

Images in this article missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at info@codegain.com

 

Introduction

Most of us when dealing with WPF applications must have been using MVVM pattern where you want to completely separate the presentation layer into a View Models. Well, it would be hard to create MVP or MVVM pattern yourself in other applications, but WPF has inbuilt support of MVVM with Command interfaces and Binding.  Binding is the concept which lets you to update the control whenever the underlying data object is modified and vice versa.

Most of the controls works great with Binding and hence can easily be used with MVVM pattern, but RadioButton has serious issue with it. In this post I will describe the problem with Radios and define some of the ways to solve it.

public class MainModel : INotifyPropertyChanged
    {

        private string _radio1 = "Radio1";
        public string Radio1
        {
            get { return this._radio1; }
            set
            {
                this._radio1 = value;
                this.OnPropertyChanged("Radio1");
            }
        }

        private bool _radio1IsCheck;
        public bool Radio1IsCheck
        {
            get { return this._radio1IsCheck; }
            set
            {
                this._radio1IsCheck = value;
                this.OnPropertyChanged("Radio1IsCheck");
                this.OnPropertyChanged("TextValue");
            }
        }

        private bool _radio2IsCheck;
        public bool Radio2IsCheck
        {
            get { return this._radio2IsCheck; }
            set
            {
                this._radio2IsCheck = value;
                this.OnPropertyChanged("Radio1IsCheck");
                this.OnPropertyChanged("TextValue");
            }
        }

        private string _radio2 = "Radio2";
        public string Radio2
        {
            get { return this._radio2; }
            set
            {
                this._radio2 = value;
                this.OnPropertyChanged("Radio2");
            }
        }

        public string TextValue
        {
            get
            {
                string selected = this.Radio1IsCheck ? this.Radio1 : this.Radio2;
                return string.Format("You have selected {0}", selected);
            }
        }
        #region INotifyPropertyChanged Members

        private void OnPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

This represents the ViewModel. Now lets bind the elements in XAML.

<Window x:Class="TestRadioBug.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestRadioBug"
    Title="Window1" Height="300" Width="300">
    <Window.DataContext>
        <local:MainModel />
    </Window.DataContext>
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <RadioButton Content="{Binding Radio1}"
                     IsChecked="{Binding Radio1IsCheck}"
                     GroupName="grp"
                     Grid.Row="0"
                     Grid.Column="0"/>
        <RadioButton Content="{Binding Radio2}"
                     IsChecked="{Binding Radio2IsCheck}"
                     GroupName="grp"
                     Grid.Row="0"
                     Grid.Column="1"/>
        <TextBlock Text="{Binding TextValue}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"/>

    </Grid>
</Window>

Now the above code displays two radiobuttons with a common group. The common group will automatically unselect one element when another is selected. Each of the RadioButton is bound to their individual properties, so that when one button is UnChecked the underlying property will be set to false.

Now there is a serious issue with this in WPF 3.5. Its a known bug in the system, which removes the Bindings altogether whenever RadioButton is used with common groups.

Just run this code in WPF 3.5 you will see the problem. The problem actually happens as the control is going to literally set the IsChecked property of all other RadioButtons in the same group when you click on one RadioButton. If you look the RadioButton class in Reflector, you will get the position where the RadioButton.IsCheck is set. And hence removes the existing Binding on the Control.

 

 

Image Loading

Inside the RadioButton class, the OnChecked event is handled check every other control which is on the same group. Thus you can see in the above code, the control Traverses the Visual Tree to find all other RadioButtons and Uncheck each of them individually.

This is the reason why Binding is getting removed from the RadioButton.

To solve this issue there are a number of options available. Lets name a few in this article.

1. Use it as Checkbox

Yes, This seems to be my first preference. When there is a problem with the existing code, it is better to create this of your own. Try not to set the GroupName for the RadioButton, this will ensure that UpdateRadioButtonGroup does not find any RadioButton on the same group to update, and you can eventually set all RadioButton IsChecked property yourself from Model.

private bool _radio1IsCheck;
public bool Radio1IsCheck
{
    get { return this._radio1IsCheck; }
    set
    {
         this.SetOtherRadioToFalse();
         this._radio1IsCheck = value;
         this.OnPropertyChanged("Radio1IsCheck");
         this.OnPropertyChanged("TextValue");
              
    }
}

2. Overriding OnChecked and OnToggle

Yes the second approach should be to create a new derived control from RadioButton and override these two methods. As I have already shown you that RadioButton resets the other radios on the group only from OnChecked and OnToggle methods, overriding it to your own will stop such behavior. In this way your button will not respond to groupnames.

public class GreatRadioButton : RadioButton
{
     protected override void OnChecked(RoutedEventArgs e)
     {
          //No code Required
     }

     protected override void OnToggle()
     {
          //No code Required
     }
}

3. Use RadioButton as List

Well, as a 3rd option, you can use your RadioButton inside a ListBox. A RadioButton List can be used to handle the ListBox. Yes, you can use binding to Bind IsSelected property of the ListBoxItem to the IsChecked property of the RadioButton, and it will work great.

<ControlTemplate TargetType="ListBoxItem"> 
    <Grid Margin="2"> 
         <Grid.ColumnDefinitions> 
           <ColumnDefinition Width="Auto" /> 
            <ColumnDefinition /> 
          </Grid.ColumnDefinitions> 
            <RadioButton IsChecked="{Binding IsSelected, 
                         RelativeSource={RelativeSource TemplatedParent}, 
                         Mode=TwoWay}" /> 
           <ContentPresenter Grid.Column="1" Margin="2,0,0,0" /> 
    </Grid> 
</ControlTemplate>

Well there are few options too like this or this, which you can also use if you like.

Summary

I must conclude, the bug is fixed in VS 2010, so if you are using 2010, you should be less bothered with this bug. Thanks a ton to MS team for fixing this issue.

 
Sign Up to vote for this article
 
About Author
 
abhi2434
Occupation-Not Provided
Company-Not Provided
Member Type-Senior
Location- Not Provided
Joined date-22 Oct 2009
Home Page-Not Provided
Blog Page-Not Provided
 
 
Other popularSectionarticles
Comments
There is no comments for this articles.
Leave a Reply
Title:
Display Name:
Email:
(not display in page for the security purphase)
Website:
Message:
Please refresh your screen using Ctrl+F5
If you can't read this number refresh your screen
Please input the anti-spam code that you can read in the image.
^ Scroll to Top