How To Set The Correct RadioButton.IsChecked Prope

2019-07-16 22:35发布

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.

2条回答
【Aperson】
2楼-- · 2019-07-16 22:58

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?

查看更多
Evening l夕情丶
3楼-- · 2019-07-16 23:09

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)

查看更多
登录 后发表回答