I reproduced this issue in a pretty simple .NET 4.0 test project.
Using MVVM, I have a view with a user control on it. When the user clicks a button, I need to replace the user control’s data context so it points to another instance of the same class. When that happens, the value of a bound radio button in the user control is reset, i.e. the ConvertBack method of my converter BooleanToRadioButtonConverter is called with a value of false.
My questions are: why does this happen and how do I work around it?
I want to preserve the state of the ViewModels and be able to switch back and forth.
Here is my test project’s code:
The parent view:
<StackPanel>
<converters:RadioButtons DataContext="{Binding RadioButtonVM, Mode=OneWay}" />
<TextBlock Margin="20 0 0 0" VerticalAlignment="Bottom">
<Hyperlink Command="{Binding SwitchViewModel}" >
<Run Text="Switch ViewModel" />
</Hyperlink>
</TextBlock>
</StackPanel>
The parent ViewModel:
public class ParentViewModel : ViewModelBase
{
private ChildViewModel _vm1;
private ChildViewModel _vm2;
private ChildViewModel _currentChildVM;
public ParentViewModel()
{
_vm1 = new ChildViewModel();
_vm2 = new ChildViewModel();
_currentChildVM = _vm1;
}
public ChildViewModel RadioButtonVM
{
get { return _currentChildVM; }
}
public ICommand SwitchViewModel
{
get
{
return new RelayCommand(p =>
{
if (_currentChildVM == _vm1)
_currentChildVM = _vm2;
else
_currentChildVM = _vm1;
// Update the view
OnPropertyChanged("RadioButtonVM");
});
}
}
The child view:
<StackPanel Orientation="Horizontal">
<RadioButton Name="IsProtectedGroupFalse" GroupName="IsProtectedGroup" Margin="10 0 0 0"
IsChecked="{Binding IsProtected, Mode=TwoWay, Converter={StaticResource BooleanToRadioButtonConverter}, ConverterParameter=false}">
<Label Content="False" Target="{Binding ElementName=IsProtectedGroupFalse}" />
</RadioButton>
<RadioButton Name="IsProtectedGroupTrue" GroupName="IsProtectedGroup" Margin="10 0 0 0"
IsChecked="{Binding IsProtected, Mode=TwoWay, Converter={StaticResource BooleanToRadioButtonConverter}, ConverterParameter=true}">
<Label Content="True" Target="{Binding ElementName=IsProtectedGroupTrue}" />
</RadioButton>
</StackPanel>
The child ViewModel:
public class ChildViewModel : ViewModelBase
{
bool _isProtected;
public bool IsProtected
{
get
{
return _isProtected;
}
set
{
if (_isProtected == value)
return;
_isProtected = value;
OnPropertyChanged("IsProtected");
}
}
}
The converter:
class BooleanToRadioButtonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Debug.WriteLine(" " + DateTime.Now.ToString("HH:mm:ss:fff") + " BooleanToRadioButtonConverter::Convert");
bool param = bool.Parse(parameter.ToString());
if (value == null)
return DependencyProperty.UnsetValue;
else
return !((bool)value ^ param);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine(" " + DateTime.Now.ToString("HH:mm:ss:fff") + " BooleanToRadioButtonConverter::ConvertBack, value=" + value.ToString() + ", parameter=" + parameter.ToString());
bool param = bool.Parse(parameter.ToString());
if (value == null)
return DependencyProperty.UnsetValue;
else
return !((bool)value ^ param);
}
}
Update
Following Rachel’s advice I replaced the RadioButtons with a CheckBox where the problem described above does not occur. I still do not know why it works the way it does, though.
I fixed the issue for the time being by adding an “active” property to the child ViewModel and manually setting that to true or false depending on whether the ViewModel is currently bound to the View or not. I do not consider that an ideal solution, though.