0

I'm trying to bind a custom class to a group of 3 Radiobuttons in my WPF app. There should be three possibilities of the class being returned, depending on which button of the group is selected. So i.e.

public class RadioButtonResult
{
public bool Istrue {get; set;}
public string WhichOne {get; set;}
}

should be bound to the 3 radiobuttons in the sense that Button 1 returns

new RadioButtonResult { Istrue = false, WhichOne = "First"}

second one returns an Instance with Istrue = true, etc... I need this because there are 3 possible situations and the element has to bind to both a boolean property and a string property.

I tried using a converter

    public class RadioButtonConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            switch (parameter)
            {
                case "First":
                    return new RadioButtonResult(false, "First");
                case "Second":
                    return new RadioButtonResult(true, "Second");
                case "Third":
                    return new RadioButtonResult(true, "First");
                default:
                    return new RadioButtonResult(false, "None")
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        { return null; }
    }

The radiobuttons themselves should have no text, so I'm not quite sure how to pass the converter parameter to even try this. (I didn't try the ConvertBack yet as I couldn't get the Convert to work)

<RadioButton GroupName="Group1" IsChecked="{Binding TestStatus, Converter=RadioButtonConverter, ConverterParameter="First"}"/>

I tried something like this, but it won't accept text as the parameter. How could I make this converter work?

JohnH
  • 45
  • 8
  • Why converter? You don't need converter here. Rather a proper view model with properties returning `RadioButtonResult` instances. [Here is an MVVM example](https://stackoverflow.com/a/37501162/1997232) with binding to `bool` property pair of radio buttons. – Sinatr Aug 13 '20 at 07:03
  • I would need about 30 rows of those same Radiobuttons, so I figured that I would need one. But the main reason why I used a converter is that I have no idea how Radiobuttons work in WPF and the design team wants them anyway. My viewmodel returns the instances since I previously implemented them with checkboxes and comboboxes, but now it's supposed to be just 3 Radiobuttons. I don't know how to bind both a string and a bool to a group of 3 Radiobuttons – JohnH Aug 13 '20 at 07:11
  • *"would need about 30 rows"* - then you need to bind to a collection. See [this answer](https://stackoverflow.com/a/2285732/1997232). – Sinatr Aug 13 '20 at 07:13
  • Well yes, but before I would tackle that, I would still need to figure out how to bind the class to the radiobuttons. How can you bind 2 properties, with 3 different outcomes, to a group of radiobuttons? – JohnH Aug 13 '20 at 07:22
  • So what you want here is one class which returns some result. There is logic depends on which of the radiobuttons is true. And you should have several radiobuttons with text, whose input is used in the logic returned from the parent class. Is that right? – Andy Aug 13 '20 at 08:28
  • @Andy basically I have 3 cases that I need to set with the radiobuttons and I would like to bind those to the existing variables. So i.e. if radiobutton1 is checked, then return an Instance of the class with (false and "One"), if radiobutton2 is checked, return an Instance with ("true" and "Two"), etc. – JohnH Aug 13 '20 at 08:54

3 Answers3

0

I don't know how to bind both a string and a bool to a group of 3 Radiobuttons

You can have 6 properties in view model or reuse RadioButtonClass class (consider to implement INotifyPropertyChanged if you want to change values dynamically)

public class ViewModel
{
    public RadioButtonResult Button1 { get; } = new RadioButtonResult(false, "First");
    ... // more buttons
}

<RadioButton GroupName="Group1"
             IsChecked="{Binding Button1.Istrue}"
             Content="{Binding Button1.WhichOne}" />

public MainWindow()
{
    InitializeComponents();
    DataContext = new ViewModel();
}
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Hm is there a way to invert the binding of the IsChecked? I would need to have "false" for the Result when the first Radiobutton is checked. I.e. if Radiobutton 1 is checked, the property would need to be (false, "First") – JohnH Aug 13 '20 at 07:37
  • This is a good case to write [InverseBooleanConverter](https://stackoverflow.com/a/1039681/1997232). – Sinatr Aug 13 '20 at 07:41
0

I think your viewmodel structure should probably be more like:

  public class RadioButtonResult
  {
      public bool Istrue {get; set;}
      public string WhichOne {get; set;}
      public List<OptionVM> Options {get; set;}
  }

OptionVM would have two properties:

      public bool Istrue {get; set;}
      public string Descriptor {get; set;}

The setter of IsTrue should initiate some logic.

Hence:

     private bool isTrue = false;
     public bool Istrue 
     { get => isTrue;
       set { isTrue = value;
             SetTheParentValue();
           }
     }

SetTheParentValue should be an Action which you inject to the viewmodel. This takes a reference to RadioButtonResult and makes istrue there true or false and sets WhichOne. So you also want a public Action SetTheParentValue on that.

And you should implement inotifypropertychanged.

No converter.

And your logic goes in the action so the classes are re-usable for other groups of radiobuttons.

We don't know enough about your overall structure to give advice on other aspects.

However.

Sets of radiobuttons are a repeating group and that's handled in wpf by binding the itemssource of an itemscontrol to a list or observablecollection of viewmodels. Here that would be of OptionVM. The data is then data templated out into radio buttons.

The ischecked property of the radio button would be bound to IsTrue and the content to Descriptor.

Andy
  • 11,864
  • 2
  • 17
  • 20
0

Rather than having a separate boolean property for each radio button, I'd suggest having a single enum value for each radio button group.

To do the binding, you would then need a converter between the enum value and the boolean IsChecked property of the radio button.

public class perValueEqualsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return parameter != null && parameter.Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value != null && value.Equals(true) ? parameter : Binding.DoNothing;
    }
}

The enum property is defined in the ViewModel ...

public enum MyEnum
{
    Value1,
    Value2,
    Value3
}

public class MainViewModel : ViewModelBase
{
    private MyEnum _e = MyEnum.Value2;

    public MyEnum E
    {
        get => _e;
        set => Set(nameof(E), ref _e, value);
    }
}

... and then used as the binding source in the View, via a converter instance

<Window 
...>

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <Window.Resources>
        <conv:perValueEqualsConverter x:Key="ValueEqualsConverter" />
    </Window.Resources>

    <Grid Margin="24">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="16" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid Grid.Row="0"
              Width=200>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="8" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="8" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <RadioButton
                Grid.Row="0"
                Content="Value 1"
                IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value1}}" />
            <RadioButton
                Grid.Row="2"
                Content="Value 2"
                IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value2}}" />
            <RadioButton
                Grid.Row="4"
                Content="Value 3"
                IsChecked="{Binding E, Converter={StaticResource ValueEqualsConverter}, ConverterParameter={x:Static local:MyEnum.Value3}}" />
        </Grid>

        <Border
            Grid.Row="0"
            Margin="-8"
            BorderBrush="Black"
            BorderThickness="2"
            CornerRadius="8" />

        <TextBlock
            Grid.Row="2"
            Text="{Binding E}" />

    </Grid>
</Window>
Peregrine
  • 4,287
  • 3
  • 17
  • 34