4

I would like to bind a property in my viewmodel to several RadioButtons (in the same group) in a Windows Forms application, using ReactiveUI.

I found several examples with WPF but no examples with Windows Forms.

EDIT: I would like to avoid creating 1:1 mapping between ViewModel property and group of radio buttons. I'd like all RadioButtons in the same group to bind to a single property and that property have the value updated every time the selection changes within the group

AIDANDan
  • 93
  • 1
  • 7

5 Answers5

2

After implementing IViewFor<YourViewModel> on your view

this.Bind(ViewModel, vm => vm.Option1, view => view.radioButton1.Checked);
//...
this.Bind(ViewModel, vm => vm.OptionN, view => view.radioButtonN.Checked);

should work.

ds-b
  • 361
  • 1
  • 5
  • 1
    This is what I would like to avoid: Creating 1:1 mapping between viewmodel property and group of radio buttons. I'd like all RadioButtons in the same group to bind to a single property and that property have the value updated every time the selection changes within the group – AIDANDan Dec 18 '15 at 21:43
1

I've just found this question while starting to learn ReactiveUI.

I agree that having a group of radio buttons bound to a single property is preferable when these are used to control an underlying scalar variable. A RadioButtonGroup control might be a "great" solution, but strikes me as requiring quite a bit of work. As a simpler solution I have written a helper class, which can be set up with more-or-less declarative code:

namespace ReactiveUIHelpers
{
  using System;
  using System.Collections.Generic;
  using System.Windows.Forms;

  public class RadioButtonHelper<TValue> where TValue : struct
  {
    private RadioButton defaultRadioButton;
    private Dictionary<RadioButton, TValue> radioButtonValues;
    private Dictionary<TValue, RadioButton> valueRadioButtons;
    private Action<TValue> setter;

    public RadioButtonHelper()
    {
      this.defaultRadioButton = null;
      this.radioButtonValues = new Dictionary<RadioButton, TValue>();
      this.valueRadioButtons = new Dictionary<TValue, RadioButton>();
    }

    public void Register(RadioButton radioButton, TValue value)
    {
      if (this.setter != null)
      {
        throw new ApplicationException($"{nameof(this.Register)} must not be called after {nameof(this.Bind)}!");
      }

      this.defaultRadioButton = this.defaultRadioButton ?? radioButton;
      this.radioButtonValues.Add(radioButton, value);
      this.valueRadioButtons.Add(value, radioButton);
      radioButton.CheckedChanged += this.RadioButton_CheckedChanged;
    }

    public void Bind(IObservable<TValue> observable, Action<TValue> setter)
    {
      if (observable == null)
      {
        throw new ArgumentNullException(nameof(observable));
      }

      if (setter == null)
      {
        throw new ArgumentNullException(nameof(setter));
      }

      this.setter = setter;
      observable.Subscribe(value => this.ValueChanged(value));
    }

    private void ValueChanged(TValue value)
    {
      RadioButton radioButton;
      if (this.valueRadioButtons.TryGetValue(value, out radioButton))
      {
        radioButton.Checked = true;
      }
      else
      {
        this.defaultRadioButton.Checked = true;
      }
    }

    private void RadioButton_CheckedChanged(object sender, EventArgs e)
    {
      RadioButton radioButton = sender as RadioButton;
      if (radioButton == null)
      {
        return;
      }

      if (!radioButton.Checked)
      {
        return;
      }

      TValue value;
      if (this.radioButtonValues.TryGetValue(radioButton, out value))
      {
        if (this.setter == null)
        {
          throw new ApplicationException($"{nameof(this.Bind)} has not been called!");
        }

        this.setter(value);
      }
    }
  }
}

And an example of usage, which would be in/called from a Form constructor, is:

this.loadingQueueStateHelper = new RadioButtonHelper<QueueStates>();
this.loadingQueueStateHelper.Register(this.radioButtonLoadingQueueEmpty, QueueStates.E);
this.loadingQueueStateHelper.Register(this.radioButtonLoadingQueueMedium, QueueStates.M);
this.loadingQueueStateHelper.Register(this.radioButtonLoadingQueueFull, QueueStates.F);
this.loadingQueueStateHelper.Bind(this.WhenAnyValue(view => view.ViewModel.LoadingQueueState),
    value => this.ViewModel.LoadingQueueState = value);
Martin Fay
  • 79
  • 3
0

I have a tri-state boolean bound to a set of radio buttons. This is ad-hoc (and a little bit verbose) but it solved the issue for me. I have the following in my form constructor:

this.WhenAnyValue( x => x.ViewModel.SomeBoolean )
    .Subscribe( someBoolean =>
    {
        if ( someBoolean == true )
            Option1.Checked = true;
        else if ( someBoolean == false )
            Option2.Checked = true;
        else
            Option3.Checked = true;
    });

this.WhenAnyValue( x => x.Option1.Checked, x => x.Option2.Checked, x => x.Option3.Checked )
    .Subscribe( values =>
    {
        var option1 = values.Item1;
        var option2 = values.Item2;
        var option3 = values.Item3;

        if ( option1 ) ViewModel.SomeBoolean = true;
        if ( option2 ) ViewModel.SomeBoolean = false;
        if ( option3 ) ViewModel.SomeBoolean = null;
    });
Mitkins
  • 4,031
  • 3
  • 40
  • 77
0
 this.WhenAnyValue(x => x.ViewModel.DetailObject.PettyCashType)
   .Subscribe(type => {
     if (type == "ap")
       RbAPType.IsChecked = true;
     else if (type == "rent")
       RbRentType.IsChecked = true;
     else RbGLType.IsChecked = true;
   }).DisposeWith(disposal);

 Observable.Merge(
     this.WhenAnyValue(x => x.RbAPType.IsChecked).Select(v => "AP"),
     this.WhenAnyValue(x => x.RbGLType.IsChecked).Select(v => "GL"),
     this.WhenAnyValue(x => x.RbRentType.IsChecked).Select(v => "Rent"))
   .Subscribe(value => ViewModel.DetailObject.PettyCashType = value)
   .DisposeWith(disposal);
0

With ModernWpf.Controls.RadioButtons, you could do like this.

It made ViewModel's enum type bind to view. The Default Top Bottom are RadioButton type declared in xaml.

this.Bind(ViewModel,
    vm => vm.Position,
    v => v.RadiosButtons.SelectedItem,
    type => type switch
    {
        Position.None => Default,
        Position.Top => Top,
        Position.Bottom => Bottom,
        _ => throw new NotSupportedException()
    },
    radioButton =>
        radioButton == null ? Position.None : // Just hack
        radioButton == Default ? Position.None :
        radioButton == Top ? Position.Top :
        radioButton == Bottom ? Position.Bottom :
        throw new NotSupportedException()
    ).DisposeWith(d);

This is wpf not winform, sry I dont't notice the title. But I think it can be made a similar one.