Darek
Darek

Reputation: 4797

ReactiveUI 6.0 and WinForms binding

Now that ReactiveUI 6.0 has been released, I have a question to the commuity: what is the best or most efficient way to bind ReactiveObject(s) and Windows Form(s).

Here is what I have so far:

My Model

namespace WindowsFormsApplication1
{
    #region

    using System;
    using System.Reactive.Linq;

    using ReactiveUI;

    #endregion

    public class ClockModel : ReactiveObject
    {
        #region Fields

        private DateTime currentDateTime;

        #endregion

        #region Constructors and Destructors

        public ClockModel() { Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(s => this.CurrentDateTime = DateTime.Now); }

        #endregion

        #region Public Properties

        public DateTime CurrentDateTime
        {
            get { return currentDateTime; }
            set { this.RaiseAndSetIfChanged(ref currentDateTime, value); }
        }

        #endregion
    }
}

My only form

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        readonly ClockModel model = new ClockModel();
        public Form1()
        {
            InitializeComponent();
            this.label1.DataBindings.Add("Text", model, "CurrentDateTime");
        }
    }
}

It does work, but I am not thrilled about referring to properties on each side by their string names. Is there a better way to accomplish this?

UPDATE 1

Given this extension:

public class NameOf<T>
{
    public static string Property<TProp>(Expression<Func<T, TProp>> expression)
    {
        var body = expression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("'expression' should be a member expression");
        return body.Member.Name;
    }
}

Is this form any more desirable than the previous one?

this.label1.DataBindings.Add(NameOf<Form1>.Property(e => e.label1.Text), model, NameOf<ClockModel>.Property(p => p.CurrentDateTime));

UPDATE 2

Another extension:

public static class Extensions
{
    public static string GetPropertyName<T,TProp>(this T obj, Expression<Func<T, TProp>> expression)
    {
        var body = expression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("'expression' should be a member expression");
        return body.Member.Name;
    }
}

Now I can use:

this.label1.DataBindings.Add(this.GetPropertyName(e => e.label1.Text), model, model.GetPropertyName(p => p.CurrentDateTime));

or this:

this.label2.DataBindings.Add(this.label2.GetPropertyName(e => e.Text), model, model.GetPropertyName(p => p.Ticks));

I start to like this one, but what do others think of any of the three approaches?

Technically speaking, the Form became a View and a Controller or Presenter, while the Model is still separated and has no knowledge of its consumers. Would this be preferred approach?

UPDATE 3

This might cause some purists a mild heart attack ... Based on Paul Betts advice:

namespace WindowsFormsApplication1
{
    #region

    using System.Windows.Forms;

    using ReactiveUI;

    #endregion

    public partial class Form1 : Form, IViewFor<ClockModel>
    {
        #region Constructors and Destructors

        public Form1()
        {
            InitializeComponent();
            this.ViewModel = new ClockModel();
            this.Bind(ViewModel, vm => vm.CurrentDateTime, v => v.label1.Text);
            this.Bind(ViewModel, vm => vm.Ticks, v => v.label2.Text);
        }

        #endregion

        #region Public Properties

        public ClockModel ViewModel { get; set; }

        #endregion

        #region Explicit Interface Properties

        object IViewFor.ViewModel
        {
            get { return ViewModel; }
            set { ViewModel = value as ClockModel; }
        }

        #endregion
    }
}

Interesting change in behavior: prior to Update 3, time was displayed with seconds. I can only reason that this is due to the default ReactiveUI converter used in the third approach. I have to learn more about it.

public class DateTimeStringConverter : IBindingTypeConverter
{
    public int GetAffinityForObjects(Type fromType, Type toType) { return 1; }

    public bool TryConvert(object @from, Type toType, object conversionHint, out object result) { 
        result = ((DateTime)@from).ToString("F");
        return true;
    }
}

I like the elegance of the approach.

Any other opinions?

Upvotes: 3

Views: 4800

Answers (1)

Ana Betts
Ana Betts

Reputation: 74654

The easiest way is to use ReactiveUI bindings - implement IViewFor<TViewModel> on your Form, then you will get several new methods such as Bind and OneWayBind.

Check out https://github.com/reactiveui/ReactiveUI/blob/docs/docs/basics/bindings.md for more information

Upvotes: 2

Related Questions