WPF RadioButton known issue: why the old property value seems still binding after property changed

1k Views Asked by At

Thanks. I hope I could upload the whole solution for I have prepared one before open this question. but I didn't find how. I will copy all the code as posible as I can here.

The question is: I think this is an easy replicated issue within WPF. I made a simple version of code to explain my question clearly. The container 'TwoPersons' in the sample has two instances of class 'Person'. The 'Current' property will be data context of a detail panel which populates person's detail info. I can use Current setter method to jump between different persions. I can see the details changing accordingly. The question is during the switching and populating current person, UI controls changes (like change male to female), then the change affects PREVIOUS item it was binding. The persion instance will be modified unexpectly. It seems previous instance still has connection with the view after new instance bound. How can I avoid this situation. Any help will be appreciated.

========================================================================

This is MainWindow.xaml.cs

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    TwoPersons twoPersons = new TwoPersons();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = twoPersons;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        twoPersons.Next();
    }
}

The container with two persons:

public class TwoPersons : INotifyPropertyChanged
{
    Person p1 = new Person("Mary") { IsFemale = true };
    Person p2 = new Person("Luis") { IsMale = true };

    Person current;

    public Person Current
    {
        get
        {
            if (current == null)
                current = p1;

            return current;
        }

        set
        {
            if (current != value)
            {
                current = value;
                OnPropertyChanged("Current");
            }
        }
    }

    public void Next()
    {
        Current = Current == p1 ? p2 : p1;
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

The person class:

public class Person : INotifyPropertyChanged
{
    public Person(string name)
    {
        this.name = name;
    }

    private string name;
    public string Name
    {
        get { return name; }
    }

    public bool isMale;
    public bool IsMale
    {
        get { return isMale; }
        set
        {
            if (isMale != value)
            {
                isMale = value;
                OnPropertyChanged("IsMale");
                OnPropertyChanged("IsFemale");
                OnPropertyChanged("Description");
            }
        }
    }

    public bool IsFemale
    {
        get { return !isMale; }
        set { IsMale = !value; }
    }

    public string Description
    {
        get
        {
            return isMale ? "Male" : "Female";
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

The main window xaml

<Grid>
    <StackPanel DataContext="{Binding Current}">
        <TextBlock Text="{Binding Name}"></TextBlock>
        <RadioButton IsChecked="{Binding IsMale}">Male</RadioButton>
        <RadioButton IsChecked="{Binding IsFemale}">Female</RadioButton>
        <TextBlock Text="{Binding Description}"></TextBlock>
        <Button Click="Button_Click" Height="32">Next >></Button>
        <TextBlock>Note. Mary should be alway a female! But it changes when navigating by clicking the button above </TextBlock>
    </StackPanel>
</Grid>
2

There are 2 best solutions below

1
On

When you ask a question one StackOverflow, you really should provide all of the code required to reproduce your problem. The first reason that you should do this is so that people that are trying to help you don't have to waste time reproducing your code manually.

The second, more important reason is that when I had to reproduce your error with my own code (for the bits of code that you didn't show, eg. the Person class) then I have implemented the code properly, so your example code works just fine for me.

Therefore, I can only assume that you have an error in your Person class, but as you didn't show that, I can't confirm it. My best guess is that you just didn't implement the INotifyPropertyChanged correctly in the Person class, or maybe even your main class. When you do this, you should find that your code works just fine.

0
On

Just like you, I initially thought your problem was caused by the order in which the bindings are executed, combined with the fact that the binding mode for some properties defaults to TwoWay:

Current is now "Louis", but the binding is still looking at "Mary"

But then I replaced your two RadioButtons with a CheckBox and everything worked as expected.

So then I started searching for problems with the RadioButton control and I came across this stackoverflow question (and many others):

Radiobutton wpf binding

Replacing the RadioButtons in your example with the RadioButtonExtended found in that question's answer, and binding to IsCheckedExt instead of IsChecked, fixed the issue. So it seems your problems are caused by a bug in the RadioButton control.

<local:RadioButtonExtended IsCheckedExt="{Binding IsMale}">Male</local:RadioButtonExtended>
<local:RadioButtonExtended IsCheckedExt="{Binding IsFemale}">Female</local:RadioButtonExtended>