How To Set The Correct RadioButton.IsChecked Property True By Binding To A ViewModel?

泪湿孤枕 提交于 2020-01-04 06:00:33

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!