Binding Doesn't Occur When re-Binding RadioButtons to an Enum
While looking at solutions for tying an enum
to a group of RadioButton
s, I discovered Sam's post from a year and a half ago.
Lars' answer was exactly what I was looking for: simple and effective.
Until I started changing the object tied to the RadioButton
group. A simple version follows.
The XAML:
<Window x:Class="RadioEnum.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:re="clr-namespace:RadioEnum"
Height="200" Width="150">
<Window.DataContext>
<re:ViewModel />
</Window.DataContext>
<Window.Resources>
<re:EnumBooleanConverter x:Key="enumBooleanConverter" />
</Window.Resources>
<DockPanel>
<ComboBox DockPanel.Dock="Top" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Things}" DisplayMemberPath="Name" />
<GroupBox>
<StackPanel>
<RadioButton IsChecked="{Binding Path=Things/Choice, Converter={StaticResource enumBooleanConverter}, ConverterParameter=First}">First</RadioButton>
<RadioButton IsChecked="{Binding Path=Things/Choice, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Second}">Second</RadioButton>
<RadioButton IsChecked="{Binding Path=Things/Choice, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Third}">Third</RadioButton>
</StackPanel>
</GroupBox>
</DockPanel>
</Window>
Now, the C#:
namespace RadioEnum
{
public class ViewModel {
public ObservableCollection<Thing> Things { get; set; }
public ViewModel() {
Things = new ObservableCollection<Thing> {
new Thing{ Name = "Thing1", Choice = Choice.First, },
new Thing{ Name = "Thing2", Choice = Choice.Second, },
};
}
}
public class Thing {
public string Name { get; set; }
public Choice Choice { get; set; }
}
public enum Choice { None, First, Second, Third, }
public class EnumBooleanConverter : IValueConverter {
// Yes, there are slight differences here from Lars' code, but that
// was to ease debugging. The original version has the same symptom.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
object ret = DependencyProperty.UnsetValue;
var parameterString = parameter as string;
if (parameterString != null && Enum.IsDefined(value.GetType(), value)) {
object parameterValue = Enum.Parse(value.GetType(), parameterString);
ret = parameterValue.Equals(value);
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
object ret = DependencyProperty.UnsetValue;
var parameterString = parameter as string;
if (parameterString != null && !value.Equals(false))
r开发者_JAVA百科et = Enum.Parse(targetType, parameterString);
return ret;
}
}
}
When the application loads with Thing1 in the ComboBox
, the correct Choice is selected in the radio group. Selecting Thing2 from the ComboBox
correctly updates the Choice. But, from this point, switching no longer updates the binding to the Second RadioButton
and thus no longer calls the Convert
method with parameter
set to "Second".
In other words, although Thing2's values have not changed, all of the RadioButton
s are cleared from that point forward. Thing1 continues to work, though.
There are no errors seen - neither exceptions nor messages in the Output window. I've tried binding in different ways. I tried making Choice a DependencyProperty
, too (and Thing
then a DependencyObject).
Any insights out there?
Original Response: Not sure if this will fix your problem or not... as I think the break in the binding might be somewhere with your combobox... but to improve on your EnumConverter and make sure it's working properly... I suggest taking a look at my response to this question:
How to bind RadioButtons to an enum?
(Not the selected answer... but my response with the generic converter rather than converting string values)
Edit: I just took your code and tried it and everything seemed to work great! (using visual studio 2010 .net 4)
You have a list of Things (in your combobox) and can set the currently selected Thing's choice via radio button. I can modify each Thing's choice and when I switch between Things it correctly updates the radio button for me!
Correct me if I am wrong on the desired functionality:
App Loads - ComboBox: Thing1 RadioButton: First
Select Thing2 - ComboBox: Thing2 RadioButton: Second
Select Thing1 - ComboBox: Thing1 RadioButton: First
Select Third - ComboBox: Thing1 RadioButton: Third
Select Thing2 - ComboBox: Thing2 RadioButton: Second
Select First - ComboBox: Thing2 RadioButton: First
Select Thing1 - ComboBox: Thing1 RadioButton: Third
Select Thing2 - ComboBox: Thing2 RadioButton: First
Above is the functionality I get when running your app with the code you provided (and with the modified EnumConverter). This also appears to be the desired result. Is the above correct and does that not work that way for you?
Edit 2: I can confirm that the issue is with .NET 3.5
I run .NET 4 Client profile... everything works as desired... running .NET 3.5 Client profile... I get the result you stated.
For those of you who may be stuck with doing this in .NET 3.5, I do have something working. It's not nearly as elegant as the code above, but it functions.
I'm more than happy to see some feedback from others on alternative methods, too. The example code below is for a ThingB that functions in both .NET 3.5 and 4.
First, change the XAML on the RadioButtons
as follows (note that the GroupName must be different for each):
<RadioButton GroupName="One" IsChecked="{Binding Path=Things/ChoiceOne}">First</RadioButton>
<RadioButton GroupName="Two" IsChecked="{Binding Path=Things/ChoiceTwo}">Second</RadioButton>
<RadioButton GroupName="Three" IsChecked="{Binding Path=Things/ChoiceThree}">Third</RadioButton>
Second, the ThingB code:
public class ThingB : INotifyPropertyChanged {
public string Name { get; set; }
public Choice Choice {
get {
return choiceOne ? Choice.First
: choiceTwo ? Choice.Second
: choiceThree ? Choice.Third : Choice.None;
}
set {
choiceOne = Choice.First.Equals(value);
choiceTwo = Choice.Second.Equals(value);
choiceThree = Choice.Third.Equals(value);
}
}
private bool choiceOne;
public bool ChoiceOne {
get { return choiceOne; }
set {
if(value) {
Choice = Choice.First;
NotifyChoiceChanged();
}
}
}
private bool choiceTwo;
public bool ChoiceTwo {
get { return choiceTwo; }
set {
if (value) {
Choice = Choice.Second;
NotifyChoiceChanged();
}
}
}
private bool choiceThree;
public bool ChoiceThree {
get { return choiceThree; }
set {
if (value) {
Choice = Choice.Third;
NotifyChoiceChanged();
}
}
}
private void NotifyChoiceChanged() {
OnPropertyChanged("ChoiceOne");
OnPropertyChanged("ChoiceTwo");
OnPropertyChanged("ChoiceThree");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
精彩评论