Search code examples
xamarin.iosxamarinmvvmcross

MVVMCross updating binding to UITableViewCell


I'm wondering if I'm doing this the correct way - this method works, but feels somewhat 'dirty'. Essentially, a button in an MvxTableViewCell changes a parameter of the bound object, but the cell does not update to reflect the change until it's scrolled out of view and back into view (ie the cell is 'redrawn'). All the examples here are simplified, but you get the idea..

Firstly, my object:

public class Expense
{
    public decimal Amount { get; set; }
    public bool Selected { get; set; }
    public Command FlipSelected
    {
        get { return new MvxCommand(()=> this.Selected = !this.Selected); }
    }
}

Secondly, my cell (in the constructor) contains:

this.DelayBind(() =>
{
    var set = this.CreateBindingSet<HistoryCell, Expense>();
    set.Bind(this.TitleText).To(x => x.Amount);
    set.Bind(this.SelectButton).To(x=> x.FlipSelected);
    set.Bind(this.SelectButton).For(x => x.BackgroundColor).To(x => x.Selected).WithConversion(new ButtonConverter(), null);
    set.Apply();
});

And i have a valueconverter that returns the background colour of the button:

class ButtonConverter : MvxValueConverter<bool, UIColor>
{
    UIColor selectedColour = UIColor.FromRGB(128, 128, 128);
    UIColor unSelectedColour = UIColor.GroupTableViewBackgroundColor;
    protected override UIColor Convert(bool value, Type targetType, object parameter, CultureInfo culture)
    {
        return value ? selectedColour : unSelectedColour;
    }
    protected override bool ConvertBack(UIColor value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == selectedColour;
    }
}

Right, so what happens is, if i click the button in the cell, it runs the command that flips the bool value Selected, which in turn binds back to the background colour of the cell via the ButtonConverter value converter.

The problem I'm having is that the cell doesn't update straight away - only when I scroll out of view of that cell and back into view (ie the cell is redrawn). So i thought I'd just cause the cell to become 'dirty':

        this.SelectButton.TouchUpInside += (o, e) =>
        {
            this.SetNeedsDisplay();
        };

But this doesn't work. What does work is putting additional code inside the TouchUpInside event that manually changes the background colour. But I'm assuming this isn't the correct way of doing it.

Do I need to trigger RaisePropertyChanged when I change the value of Selected in the Expense object? How can I do that when it's just an object?

Really hoping Stuart can help out on this one ;)


Solution

  • I think your analysis is correct - the UI isn't updating live because there are no change messages from your Expense objects.

    To provide 'traditional' change notifications in your view model objects, you need to make sure each one supports INotifyPropertyChanged. This small interface is easy to implement yourself if you want to - or you can modify your Expense to inherit the built-in MvxNotifyPropertyChanged helper class if you prefer - then RaisePropertyChanged would be available.

    As one other alternative, you can also implement the new 'Rio' field based binding if you prefer. For an intro to this, see N=36 in http://mvvmcross.blogspot.com