Binding to ComboBox using ReactiveUI and Windows Forms
Asked Answered
B

5

5

I would like to bind a property in my viewmodel to a ComboBox in a Windows Forms application, using ReactiveUI.

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

EDIT: Part 1: Bind the selected value to Following example from comment:

this.Bind(ViewModel, vm => vm.ViewModelProperty, v => v.comboBox.SelectedValue, comboBox.Events().SelectedValueChanged);

I get the error: CS1955 Non-invocable member 'Component.Events' cannot be used like a method.

Part 2: Bind the items in the ComboBox to a collection in the viewmodel ? Don't know how to do

Bengurion answered 17/12, 2015 at 14:1 Comment(0)
Q
3

You can use the Observable.FromEventPattern method to bind the firing of the SelectedIndexChanged event to your view model property.

comboBoxWithItems.DataSource = ViewModel.ListOfPossibleItemsProperty;
comboBoxWithItems.DisplayMember = "Name";

Observable.FromEventPattern<EventHandler, EventArgs>(
    ev => comboBoxWithItems.SelectedIndexChanged += ev,
    ev => comboBoxWithItems.SelectedIndexChanged -= ev)
    .Select(x => comboBoxWithItems.SelectedItem)
    .BindTo(this, x => x.ViewModel.SelectedItemProperty);
Quade answered 18/12, 2015 at 22:1 Comment(0)
D
4

First, your view should implement IViewFor<YourViewModel> interface and then

this.Bind(ViewModel, vm => vm.PropertyToBind, x => comboBox.SelectedValue, comboBox.Events().SelectedValueChanged) 

EDIT: I have create a demo project:

using System;
using System.Reactive.Linq;
using System.Windows.Forms;
using ReactiveUI;

namespace WindowsFormsApplication
{
    public partial class Form1 : Form, IViewFor<MyViewModel>
    {
        public Form1()
        {
            InitializeComponent();

            ViewModel = new MyViewModel();
            comboBox1.DataSource = ViewModel.Items;

            var selectionChanged = Observable.FromEvent<EventHandler, EventArgs>(
                h => (_, e) => h(e),
                ev => comboBox1.SelectedIndexChanged += ev,
                ev => comboBox1.SelectedIndexChanged += ev);
            this.Bind(ViewModel, vm => vm.SelectedItem, x => x.comboBox1.SelectedItem, selectionChanged);
        }

        public MyViewModel ViewModel { get; set; }

        object IViewFor.ViewModel
        {
            get { return ViewModel; }
            set { ViewModel = (MyViewModel)value; }
        }
    }

    public class MyItem
    {
        private readonly string _text;

        public MyItem(string text)
        {
            _text = text;
        }

        public override string ToString()
        {
            return _text;
        }
    }

    public class MyViewModel : ReactiveObject
    {
        private MyItem _selectedItem;

        public MyViewModel()
        {
            Items = new ReactiveList<MyItem> {new MyItem("test1"), new MyItem("test2")};
        }

        public MyItem SelectedItem
        {
            get { return _selectedItem; }
            set { this.RaiseAndSetIfChanged(ref _selectedItem, value); }
        }

        public ReactiveList<MyItem> Items { get; private set; }
    }
}
Disapprobation answered 17/12, 2015 at 16:43 Comment(5)
Thanks mate. I get an error when I try to do that... What am I missing? (see updated question)Bengurion
Install-Package reactiveui-events-winforms ;)Disapprobation
Thanks again, but this package doesn't seem to be part of ReactiveUI... I can only use packages that are maintained by the core team. Any idea how to do that using the packages that the core team publishes?Bengurion
If you don't want to use reactiveui-events-winforms package, you can replace comboBox.Events().SelectedValueChanged with Observable.FromEvent<EventHandler, EventArgs>( ev => comboBox.SelectedValueChanged += ev, ev => comboBox.SelectedValueChanged += ev);Disapprobation
+1 Thank you. This seems to work when I change the selection (the binding happens) but when the application starts the binding doesn't happen and the combo box is empty (instead of selecting the first item). Any ideas?Bengurion
Q
3

You can use the Observable.FromEventPattern method to bind the firing of the SelectedIndexChanged event to your view model property.

comboBoxWithItems.DataSource = ViewModel.ListOfPossibleItemsProperty;
comboBoxWithItems.DisplayMember = "Name";

Observable.FromEventPattern<EventHandler, EventArgs>(
    ev => comboBoxWithItems.SelectedIndexChanged += ev,
    ev => comboBoxWithItems.SelectedIndexChanged -= ev)
    .Select(x => comboBoxWithItems.SelectedItem)
    .BindTo(this, x => x.ViewModel.SelectedItemProperty);
Quade answered 18/12, 2015 at 22:1 Comment(0)
C
0

Your initial vm.SelectedItem is null and there is no change yet to update the VM from the view. Set an initial selection in the VM constructor.

Corps answered 29/8, 2016 at 6:45 Comment(0)
S
0

A couple of ideas for improvement relating to the list of values:

  1. Replace the direct set of comboBox1.DataSource = ViewModel.Items; with a bind OneWayBind(ViewModel, vm => vm.Items, v => v.comboBox1.DataSource); so that it isn't necessary for ViewModel to exist inside the view constructor and ViewModel can be dynamically changed.
  2. Use ReactiveBindingList instead of ReactiveList so that WinForms binding can react to changes in the value list (though I haven't tried this for this exact scenario).
Seacock answered 21/10, 2016 at 8:56 Comment(0)
A
0

Since the other solutions did not work for me in UWP applications, there is a proper way that works in WinForms, WPF and UWP applications: use the Bind methods in the constructor of the view. Example for WPF/UWP:

using ReactiveUI;
using System.Reactive.Disposables;

    public sealed partial class MyView : Page, IViewFor<MyViewModel>
    {

        public MyView()
        {
            InitializeComponent();

            this.WhenActivated(d =>
            {
                this.OneWayBind(ViewModel, vm => vm.Items, v => v.DropDownControl.ItemsSource)
                    .DisposeWith(d);

                this.Bind(ViewModel, vm => vm.SelectedItem, v => v.DropDownControl.SelectedItem)
                    .DisposeWith(d);
            });
        }


        public MyViewModel ViewModel
        {
            get => DataContext as MyViewModel;
            set => DataContext = value;
        }

        object IViewFor.ViewModel
        {
            get => ViewModel;
            set => ViewModel = value as MyViewModel;
        }
    }

In ViewModel:

using ReactiveUI.Fody.Helpers;

    public sealed class MyViewModel : ReactiveObject
    {
        public void MyViewModel()
        {
            // Todo: Load items
        }

        [Reactive] public IList<MyItem> Items { get; set; } = new List<MyItem>();
        [Reactive] public MyItem? SelectedItem { get; set; }
    }
Ahmedahmedabad answered 19/4, 2020 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.