Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How does MvvmCross support data binding in custom cells for iOS


How does MvvmCross support data binding in custom cells for iOS


MvvmCross supports data binding in custom cells for iOS by utilizing its powerful data-binding engine. This engine allows developers to bind properties of custom cell views to properties in their view models. Here's how you can achieve this:

Creating Custom Cells

To start, you need to create a custom cell class that inherits from `MvxTableViewCell`. This is crucial because `MvxTableViewCell` provides the necessary infrastructure for data binding within MvvmCross.

csharp
public class CustomCell : MvxTableViewCell
{
    public static readonly NSString Key = new NSString("CustomCell");

    public CustomCell(IntPtr handle) : base(handle)
    {
        // Initialize your cell here
    }
}

Designing the Cell

You can design your cell using XIB files or storyboards. If using a storyboard, ensure that the cell's class is correctly set to your custom cell class. If using XIB, you'll need to create a XIB file for your cell and design it there.

Binding Properties

To bind properties within your custom cell, you need to expose them as public properties. For example, if your cell contains a `UILabel`, you would expose a property like this:

csharp
public string MyLabelText
{
    get { return myLabel.Text; }
    set { myLabel.Text = value; }
}

Implementing Data Binding

In your cell's constructor or `DelayBind` method, you can set up the data binding. The `DelayBind` method is used to delay the binding until the cell is fully initialized.

csharp
public override void DelayBind()
{
    base.DelayBind();

    var set = this.CreateBindingSet();
    set.Bind(this).For(c => c.MyLabelText).To(vm => vm.MyViewModelProperty);
    set.Apply();
}

Two-Way Binding

For two-way binding, MvvmCross needs to know when the UI value changes. This can be achieved by firing an event from your cell whenever the bound property changes. For example, if you're using a `UITextView`, you might need to manually handle the `TextChanged` event to notify the view model of changes.

csharp
public event EventHandler MyLabelTextChanged;

public string MyLabelText
{
    get { return myTextView.Text; }
    set
    {
        myTextView.Text = value;
        MyLabelTextChanged?.Invoke(this, EventArgs.Empty);
    }
}

// In your cell's initialization
myTextView.TextChanged += (sender, e) => MyLabelTextChanged?.Invoke(this, EventArgs.Empty);

Then, in your binding setup, you would need to listen for this event to update the view model:

csharp
set.Bind(this).For(c => c.MyLabelText).To(vm => vm.MyViewModelProperty).Mode(MvxBindingMode.TwoWay);

However, for `UITextView`, two-way binding is not directly supported out of the box in MvvmCross. You might need to implement a custom binding target to handle the `TextChanged` event properly.

Custom Binding Targets

If MvvmCross does not support a specific control or binding scenario out of the box, you can create a custom binding target. This involves defining how to bind a specific property and which event to listen for value changes.

csharp
public class MyTextViewBinding : MvxTargetBinding
{
    private UITextView _textView;

    public MyTextViewBinding(UITextView textView)
    {
        _textView = textView;
    }

    public override void SetValue(object value)
    {
        _textView.Text = (string)value;
    }

    public override Type TargetType => typeof(string);

    public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;

    public override void SubscribeToEvents()
    {
        _textView.TextChanged += OnTextChanged;
    }

    private void OnTextChanged(object sender, EventArgs e)
    {
        FireValueChanged(_textView.Text);
    }

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            _textView.TextChanged -= OnTextChanged;
        }
        base.Dispose(isDisposing);
    }
}

You would then register this custom binding target in your setup.

csharp
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    registry.RegisterCustomBindingFactory("Text", textView => new MyTextViewBinding(textView));
    base.FillTargetFactories(registry);
}

This approach allows you to extend MvvmCross's data-binding capabilities to support custom controls or scenarios not covered by default.

Citations:
[1] https://stackoverflow.com/questions/17156368/
[2] https://www.marcbruins.nl/xamarin-ios-list-with-mvvmcross
[3] https://yanxiaodi.gitbook.io/xamarin-mvvmcross-handbook/using-mvvmcross-to-create-your-first-xamarin-app/creating-the-ios-project/understanding-the-data-binding-syntax
[4] https://stackoverflow.com/questions/24362702/mvvmcross-binding-to-table-cell-in-a-storyboard
[5] https://www.mvvmcross.com/documentation/tutorials/tipcalc/a-xamarinios-ui-project
[6] https://stackoverflow.com/questions/48247969/customising-tableviewcell-in-xamarin-ios-with-mvvmcross
[7] https://stackoverflow.com/questions/22555930/mvvmcross-ios-method-binding-rio
[8] https://stackoverflow.com/questions/52820980/mvvmcross-ios-assets-binding-to-cell/53089355
[9] https://www.mvvmcross.com/documentation/advanced/custom-data-binding
[10] https://jfarrell.net/2015/03/22/binding-ios-views-with-mvvmcross/
[11] https://blog.ostebaronen.dk/2018/01/mvvmcross-binding-target.html
[12] https://www.mvvmcross.com/documentation/platform/ios/ios-user-interfaces-approaches
[13] https://www.mvvmcross.com/documentation/fundamentals/data-binding