问题
I have the following scenario where a class like this:
public class PetOwnerViewModel{
public PetOwnerStatus Status{get{return _petOwner.Status;}}
public ICommand SetStatusCommand {get{...}}
}
Is DataContext to a group of RadioButtons similar to this:
<Parent DataContext="{Binding Path=PetOwner}" >
<Parent.Resources>
<myenums:PetOwnerStatus x:Key="CATLOVER">
CatLover
</myenums:PetOwnerStatus>
<myenums:PetOwnerStatus x:Key="DOGLOVER">
DogLover
</myenums:PetOwnerStatus>
</Parent.Resources>
<StackPanel>
<RadioButton Name="catLoverRadioButton"
Command="{Binding SetStatusCommand}"
CommandParameter="{StaticResource DOGLOVER}"
GroupName="PetOwnerStatusRadioButtonGroup">
Cat Lover
</RadioButton>
<RadioButton Name="dogLoverRadioButton"
Command="{Binding SetStatusCommand}"
CommandParameter="{StaticResource CATLOVER}"
GroupName="SubjectStatusRadioButtonGroup" >
Dog Lover
</RadioButton>
</StackPanel>
</Parent>
How do I bind the View to the ViewModel so that if PetOwnerViewModel.Status returns PetOwnerStatus.CatLover, catLoverRadioButton.IsChecked is true.
回答1:
You can make this sort of thing very dynamic using data-templating, e.g.
(-- Edit: It makes a lot more sense to use a ListBox
which already has a SelectedItem
property, see this revised answer --)
public partial class MainWindow : Window, INotifyPropertyChanged
{
//For simplicity in put everything in the Window rather than models and view-models
public enum TestEnum { Ichi, Ni, San }
private TestEnum _EnumValue;
public TestEnum EnumValue
{
get { return _EnumValue; }
set
{
if (_EnumValue != value)
{
_EnumValue = value;
PropertyChanged.Notify(() => this.EnumValue);
}
}
}
//...
}
<ItemsControl>
<ItemsControl.Resources>
<!-- Gets the enum values -->
<ObjectDataProvider x:Key="items" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:MainWindow+TestEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<!-- A MultiValueConverter which compares for equality -->
<vc:EqualityComparisonConverter x:Key="eqc" />
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<Binding Source="{StaticResource items}" />
</ItemsControl.ItemsSource>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding}" GroupName="TestEnumGroup"
Command="{x:Static local:Commands.DoStuff}" CommandParameter="{Binding}">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource eqc}" Mode="OneWay">
<!-- This should point to the viewmodel enum property -->
<Binding ElementName="Window" Path="DataContext.EnumValue" />
<!-- This passes the DataContext, the enum value behind the templated item, to the converter -->
<Binding />
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void DoStuff_Executed(object sender, ExecutedRoutedEventArgs e)
{
TestEnum enumval = (TestEnum)e.Parameter;
EnumValue = enumval;
}
This operates with the raw enum values, you could augment them with display friendly strings using attributes.
Because IsChecked
is bound on all RadioButtons the RadioButton.GroupName
becomes redundant.
(I did not provide my implementation of the EqualityComparisonConverter
because it's probably crap, it shouldn't be too hard to properly implement it though)
回答2:
There's a fairly well-known bug in WPF with data binding and RadioButtons. This is the way I would normally do it:
<StackPanel>
<RadioButton
Content="Cat Lover"
Command="{Binding SetStatusCommand}"
CommandParameter="{x:Static local:PetOwnerStatus.CatLover}"
IsChecked="{Binding Path=Status, Mode=TwoWay, Converter={StaticResource equalityConverter}, ConverterParameter={x:Static local:PetOwnerStatus.CatLover}}"
GroupName="1" />
<RadioButton
Content="Dog Lover"
Command="{Binding SetStatusCommand}"
CommandParameter="{x:Static local:PetOwnerStatus.DogLover}"
IsChecked="{Binding Path=Status, Mode=TwoWay, Converter={StaticResource equalityConverter}, ConverterParameter={x:Static local:PetOwnerStatus.DogLover}}"
GroupName="2" />
</StackPanel>
The equalityConverter takes a ConverterParameter of an enum and compares it against the binding value (Status). If the values are equal, the converter returns true, which in turn sets IsChecked to true. The IsChecked binding expression above is essentially saying "if the value specified in ConverterParameter equals the value of Status, set IsChecked to true".
Also, you can use the actual enum values by defining the namespace and using x:Static, without having to create separate resources.
Note that you have to give a different GroupName to each RadioButton, otherwise, the WPF bug manifests itself and the bindings get broken.
More details available here: How to bind RadioButtons to an enum?
来源:https://stackoverflow.com/questions/6258505/how-to-set-the-correct-radiobutton-ischecked-property-true-by-binding-to-a-viewm