Search code examples
mvvmxamarinmvvmcrossfluent

Is it possible to have method binding in mvvmcross using Fluent API?


I have an object structure that looks like this.

public class Model : MvxViewModel
{    
    private IDictionary<string, string> _properties;

    public IDictionary<string, string> Properties
    {
        get { return _properties; }
    }

    public string this[string key]
    {
        get { return Get(key); }
        set { Set(key, value); ;}
    }        

    public Model()
    {
        this._properties = new Dictionary<string, string>();
    }        

    public void Set(string propertyName, string value)
    {
        if (!_properties.ContainsKey(propertyName))
        {
            _properties[propertyName].Value = value;
        }
    }

    public string Get(string propertyName)
    {
        return _properties[propertyName];
    }               
}

I need to bind information from this object to a control using Fluent API. My control is create in the code.

The code looks like this:

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.Hello);

    Model employeeModel = new Model();
    model["Id"] = 1000;
    model["FirstName"] = "Stuart";
    model["MiddleName"] = "";
    model["LastName"] = "Lodge";
    TableLayout containerLayout = this.FindViewById<TableLayout>(Resource.Id.containerLayout);
    if (containerLayout != null)
    {
        TableRow newRow = new TableRow(base.ApplicationContext);
        newRow.SetMinimumHeight(50);

        var txtFirstName = new EditText(ApplicationContext);
        txtFirstName.Hint = "First Name";

        var bindingSet = this.CreateBindingSet<HelloView, Model>();
        bindingSet.Bind(txtFirstName).For("Text").To(vm => vm.Get("FirstName"));
        bindingSet.Apply();

        newRow.AddView(txtFirstName);
        containerLayout.AddView(newRow);
    }

}

Is it possible to do something like this?


Solution

  • Is it possible to...

    Yes, MvvmCross is very extensible so you could add this if you wanted to.

    To do it, you'd need to:

    • give this scheme a name of some kind - for convenience now, let's call it Amit-Binding as Method-Binding has already been used for Auto-ICommand binding - see N=36 in http://mvvmcross.wordpress.com
    • work out it's spec a little more fully - e.g. just clarifying how changes will be published from the ViewModel
    • work out how the Amit-binding will be presented in binding descriptions - and probably best to work out how to present them in text-format bindings too
    • provide one or more extension methods which allow the FluentBinding to generate a binding description containing an Amit-Binding - this will involve parsing the Expressions using your generic parameterised method calls
    • possibly also extend the Tibet binding parser and the source property parser classes to allow it to parse the text format of these Amit-bindings (this may not be necessary if you've chosen an already-parseable format for the text version of these classes)
    • provide a source property binding extension factory which will know when and how to create these Amit-Source bindings.

    This may sound like a lot of effort - but it is actually quite doable. For an example of how INotifyChanged source binding has been added using a plugin, see https://github.com/slodge/MvvmCross/tree/v3/Plugins/Cirrious/FieldBinding/Cirrious.MvvmCross.Plugins.FieldBinding - although note that this was achieved without adding any new requirements to FluentBinding or to the parsers.


    Alternatively...

    You could just use string-indexer binding on "normal properties" or on fields (if using the FieldBinding plugin).

    For an example of this in both Touch and Droid, see the ObservableDictionary example in https://github.com/slodge/MvvmCross-Tutorials/tree/master/ApiExamples

    • the Core project includes an ObservableDictionary implementation reproduced from http://blogs.microsoft.co.il/blogs/shimmy/archive/2010/12/26/observabledictionary-lt-tkey-tvalue-gt-c.aspx (normal Dictionaries can be used if dynamic updating isn't required)

    • the Touch UI project includes a fluent binding block of:

          var set = this.CreateBindingSet<ObservableDictionaryView, ObservableDictionaryViewModel>();
          set.Bind(label1).To(vm => vm.Items["One"]);
          set.Bind(label2).To(vm => vm.Items["Two"]);
          set.Bind(label3).To(vm => vm.Items["Three"]);
          set.Bind(all).To(vm => vm.ReplaceAllCommand);
          set.Bind(each).To(vm => vm.ReplaceEachCommand);
          set.Bind(makeNull).To(vm => vm.MakeNullCommand);
          set.Apply();