WPF + MVVM + RadioButton : How to handle binding with single property?
Asked Answered
V

5

17

From this and this (and other) questions on Stack Overflow and many other material on internet, I understood how to bind radio button (option button) with View Model.

But all of them create separate property for each possible value of radio button. One question is similar to my requirement but the accepted answer suggests to use ListBox instead of radio button.

For example, to represent person gender (Datatype Char, Possible values 'M', 'F'), three properties needs to be created in View Model as PersonGender, IsPersonMale, IsPersonFemale.
I want to control this on only one property PersonGender. Can I do this? If yes, how?

Vestiary answered 13/6, 2016 at 12:36 Comment(3)
Put a Command on each RadioButton (the same) and pass their value as parameterUnconsidered
@nkoniishvt: Looks interesting; will try this. But still not fully acceptable. Radio button represents user input and display like text box or combo box; not action like Command Button. Using Command to handle input/display dose not look correct.Vestiary
Command are made for MVVM, binding to a Command from the UI is what you should do if you want to have a MVVM compliant app. Take a look at a tutorial like this one: codeproject.com/Articles/238657/How-to-use-Commands-in-WPFUnconsidered
A
34

You need a IValueConverter.

//define this in the Window's Resources section or something similiarly suitable
<local:GenderConverter x:Key="genderConverterKey" />


<RadioButton Content="M" IsChecked="{Binding Gender, Converter={StaticResource ResourceKey=genderConverterKey}, ConverterParameter=M}" />
<RadioButton Content="F" IsChecked="{Binding Gender, Converter={StaticResource ResourceKey=genderConverterKey}, ConverterParameter=F}" />

The converter

public class GenderConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((string)parameter == (string)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return (bool)value ? parameter : null;        
    }
}

Replace null (in ConvertBack) with Binding.DoNothing if binding should not be applied in that case.

return (bool)value ? parameter : Binding.DoNothing;
Avent answered 13/6, 2016 at 18:49 Comment(3)
Replace null (in ConvertBack) with Binding.DoNothing if binding should not be applied in that case. return (bool)value ? parameter : Binding.DoNothing;Vestiary
There is nothing in the converter that actually ties it to Gender data type. So you could rename it to make it a general purpose converter.Okie
For my taste, this solution is purer MVVM than the commands solution.Leonie
U
4

Use Commands (Using DelegateCommands here)

VM:

public enum Genders {
    Female,
    Male
}
public YourVMClass {
    public Genders SelectedGender {get; set;}

    private DelegateCommand _cmdSelectGender;

    public DelegateCommand CmdSelectGender {
        get { return _cmdSelectGender ?? (_cmdSelectGender = new DelegateCommand(SelectGender)); }
    }

    private void SelectGender(Object parameter) {
        SelectedGender = (Genders)parameter;
    }
}

XAML:

<Window.Resources>
    <ObjectDataProvider x:Key="listOfGenders" MethodName="GetValues"
                        ObjectType="{x:Type System:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="loca:Genders"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

<ItemsControl ItemsSource="{Binding Source={StaticResources listOfGenders}}">
    <ItemsControl.ItemTemplate>
        <RadioButton GroupName="Genders" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MainWindow}}, Path=DataContext.CmdSelectGender}" CommandParameter="{Binding}"/>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

(Didn't test the code, but this is the idea)

Unconsidered answered 13/6, 2016 at 13:30 Comment(0)
U
2

Your requirement is possible, but you would need some additional effort to make it work. The main reason for this is RadioButton’s property IsChecked is Boolean and multiple radio buttons are separate controls and doesn’t act like one. For example a ListBox.

For your requirement to work, you could use a Converter. Bind the IsChecked of both Radiobuttons to the PersonGender property in ViewModel and use a common converter and pass a parameter ‘MALE’ for male radio button and ’FEMALE’ for female radio button.

In Converter check if parameter and PersonGender are same and return TRUE for that.

Ie. If checkbox command parameter is MALE and PersonGender is also MALE, then the checkbox is enabled Else if checkbox command parameter is FEMALE and PersonGender is MALE, checkbox is not enabled since false is returned.

Unto answered 13/6, 2016 at 13:14 Comment(0)
A
1

I changed the solution converter a bit to an MarkupExtension to make it bit easier for me to use (here as int-converter):

public class RadioValueExtension : MarkupExtension, IValueConverter
{
    [ConstructorArgument("value")]
    public int Value { get; set; }

    public RadioValueExtension(int value)
    {
        Value = value;    
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Value == (int)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Value : Binding.DoNothing;
    }
}

So I can use it like this

<RadioButton IsChecked="{Binding Gender, Converter={converters:RadioValue 0}}" Content="M" />
<RadioButton IsChecked="{Binding Gender, Converter={converters:RadioValue 1}}" Content="F" />
Assurance answered 25/10, 2021 at 17:51 Comment(0)
H
0

What you need to do is using IValueConverter, that convert Bool to Char and vice versa. Example True => 'M'; False => 'F'

In your View there are two Radio buttons. Make sure to set them in the same group name. Then You need to bind your PersonGender to only IsMale radio button only. because then IsFemale radio button is checked. the IsMale radio buton will be unchecked automatically due to same group name.

Hustler answered 13/6, 2016 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.