共用方式為


WPF Data Binding with RadioButton

I recently was building a WPF solution where I had a couple RadioButtons that each represented a particular state of the data. Each RadioButton had its IsChecked dependency property bound to a property on my DataContext. And they were each bound to a Command that would change the underlying state when executed.

This is an example of what I had done:

<RadioButton GroupName="type" Command="{Binding ChangeTypeCommand}" IsChecked="{Binding Path=IsFoo, Mode=OneWay}">
<Label>Foo</Label>
</RadioButton>
<RadioButton GroupName="type" Command="{Binding ChangeTypeCommand}" IsChecked="{Binding Path=IsBar, Mode=OneWay}">
<Label>Bar</Label>
</RadioButton>

This worked fine when the control that hosted the RadioButtons was loaded. It would retrieve the values from IsFoo and IsBar (two mutually exclusive properties) and set the state of the buttons appropriately. The problem occurred after I clicked one of the buttons. The click would change the UI state of the buttons correctly (for example, clicking Bar would uncheck Foo and check Bar). But I noticed that if the underlying value of IsFoo and IsBar ever changed after that point, the buttons would not have their IsChecked state updated. Using the Snoop tool, I discovered that the IsChecked state had had its state set manually after clicking on one of the buttons. Once a dependency property has been set manually, it loses its Binding. This is why the IsChecked state was not being changed when the properties being bound to were updated.

At this point, I opened up Reflector to take a look at the code of the RadioButton class. Sure enough, there are a couple spots where the IsChecked property is set to a literal value: OnToggle and UncheckRadioButton. OnToggle is a virtual method derived from ToggleButton which is called by OnClick. So when you click a RadioButton, this causes the IsChecked to be set to true. But the other RadioButtons that belong to the same group as the clicked button must be unchecked. This is handled by the OnChecked virtual method, again derived from ToggleButton. RadioButton's OnChecked implementation calls UpdateRadioButtonGroup which unchecks all the other RadioButtons that belong to the same group as the one that was clicked.

Since I wanted the IsChecked state of my RadioButtons to be based solely on the values from the Bindings, I needed to turn off this functionality of the RadioButton. To do this, I just created my own custom version of RadioButton called DataBoundRadioButton that overrides OnToggle and OnChecked to prevent any changing of the IsChecked state. I then used this new class in place of RadioButton.

public class DataBoundRadioButton : RadioButton
{
protected override void OnChecked(RoutedEventArgs e)
{
// Do nothing. This will prevent IsChecked from being manually set and overwriting the binding.
}

protected override void OnToggle()
{
// Do nothing. This will prevent IsChecked from being manually set and overwriting the binding.
}
}