Как установить правильное свойство RadioButton.IsChecked True путем привязки к ViewModel?

У меня есть следующий сценарий, где класс вроде этого:

public class PetOwnerViewModel{
public PetOwnerStatus Status{get{return _petOwner.Status;}}

    public ICommand SetStatusCommand {get{...}}
}

Является ли DataContext для группы RadioButtons похожим на это:

<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>

Как связать View с ViewModel, чтобы, если PetOwnerViewModel.Status возвращает PetOwnerStatus.CatLover, true, catLoverRadioButton.IsChecked является истинным.

Ответ 1

Вы можете сделать такую ​​вещь очень динамичной с использованием шаблонов данных, например.

(- Изменить:. Намного больше смысла использовать ListBox, у которого уже есть свойство SelectedItem, см. этот пересмотренный ответ -)

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;
}

Это работает с необработанными значениями enum, вы можете увеличить их с помощью отображаемых строк, используя атрибуты.

Поскольку IsChecked привязан ко всем радиобарабанам, RadioButton.GroupName становится избыточным.

(Я не представил свою реализацию EqualityComparisonConverter, потому что это, вероятно, дерьмо, это не должно быть слишком сложно, чтобы правильно реализовать его, хотя)

Ответ 2

Там есть довольно известная ошибка в WPF с привязкой данных и RadioButtons. Так я обычно это делаю:

<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>

Равенство конвертера принимает преобразовательПараметр перечисления и сравнивает его со значением привязки (Статус). Если значения равны, преобразователь возвращает true, что в свою очередь устанавливает IsChecked в true. Вышеописанное выражение IsChecked по существу говорит "если значение, указанное в ConverterParameter, равно значению Status, установите IsChecked в true".

Кроме того, вы можете использовать фактические значения перечисления, указав пространство имен и используя x: Static, без необходимости создавать отдельные ресурсы.

Обратите внимание, что вы должны указывать другое имя GroupName для каждого RadioButton, в противном случае ошибка WPF проявляется, и привязки разбиваются.

Более подробная информация доступна здесь: Как связать RadioButtons с перечислением?