MVVM Light UigitiveViewCell与Xamarin.ios绑定

When ever you want to display a list or collection of information under iOS tables are often the choice you will end up using. But what if you have content that changes and you want to update the data in a cell? Well that’s what bindings are for right? But how do you use bindings in a UITableViewCell? Well let’s check it out.

欢迎来到我们的倒计时应用程序。正如您所看到的,它是一个定时器列表,它显示在单元格中并在滴定时更新。

CellBinding.

If we look at the basic setup of a displayed table, we will have UITableView which uses a UITableViewSource for managing the collection to be displayed and the rendering of the UITableViewCells. Then there is the view model which is providing a list of items, in our case timers, that should be displayed. So let’s go through each of the items from View Model to Cell.

视图模型

MVVM Light comes with helpers that allow you to convert an ObservableCollection to a UITableViewSource. Choosing this option will further ensure ensure the UI is updated when we add or remove an element to our collection. So let’s take it:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        // ...
        Countdowns = new ObservableCollection<CountdownViewItem>();
        Countdowns.Add(new CountdownViewItem(new TimeSpan(0, 13, 37)));
    }

    // ...
    public ObservableCollection<CountdownViewItem> Countdowns { get; private set; }

    // ...
}

The CountDownViewItem hold’s the state of the individual item. Hence the timer logic is embedded into it and will also implement the RaisePropertyChanged.

public class CountdownViewItem : ViewModelBase
{
    DateTime _expirationTimestamp;

    public CountdownViewItem(TimeSpan timespan)
    {
        if (timespan == null) throw new ArgumentNullException(nameof(timespan));
        _expirationTimestamp = DateTime.UtcNow + timespan;
        Countdown();
    }

    string _remainingTimeString;
    public string RemainingTimeString
    {
        get
        {
            return _remainingTimeString;
        }
        set
        {
            if (value == _remainingTimeString) return;
            _remainingTimeString = value;
            RaisePropertyChanged(nameof(RemainingTimeString));
        }
    }

    private async void Countdown()
    {
        while (DateTime.UtcNow < _expirationTimestamp)
        {
            TimeSpan remainingTime = _expirationTimestamp - DateTime.UtcNow;
            RemainingTimeString = remainingTime.ToString(@"hh\:mm\:ss");
            await Task.Delay(millisecondsDelay: 490);
        }

        RemainingTimeString = "Timer Expired";
    }
}

因此,让我们将这些视图模型放在UI中使用。

表视图

One can choose to create the table either in a story board or in code. Choosing the code path we would have a UIViewController that creates the UITableView, set’s the layout and creates the UITableSource from the view models collection:

class ViewController : UIViewController
{
    // private members

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // Setup view and layouting

        // Setup bindings
        _tableViewController = Vm.Countdowns.GetController(CreatePersonCell, BindCellDelegate);
        _tableViewController.TableView = CountdownsTableView;

        AddTimerButton.SetCommand("TouchUpInside", Vm.AddCountdownCommand);
    }

    private void BindCellDelegate(UITableViewCell cell, CountdownViewItem countdownViewItem, NSIndexPath path)
    {
        var bindableCell = (CustomCell) cell;
        bindableCell.Configure(countdownViewItem);
    }

    private UITableViewCell CreatePersonCell(NSString cellIdentifier)
    {
        return CountdownsTableView.DequeueReusableCell(nameof(CustomCell));
    }
}

注意我们正在使用自定义单元格,我们正在注册它,以便我们可以重用单元格。这将使我们的应用程序能够重用bet188地址的单元格。提高内存足迹,性能,被认为是最佳实践。我们很快就会进入该电池的细节。

如果您正在寻找故事板方法,请注意,此步骤只有此步骤会有所不同。看看 GitHub. 样本的存储库。

细胞

The cell is invoked when via the BindCellDelegate method. So we can populate our cell with binding like this:

public class CustomCell : UITableViewCell
{
    CountdownViewItem _countdownViewItem;
    // ui label and constructors

    private void InitCell()
    {
        // Layout cell
    }

    public Binding<string, string> _timerBinding;

    internal void Configure(CountdownViewItem countdownViewItem)
    {
        _countdownViewItem = countdownViewItem;
        _timerBinding = this.SetBinding(() => _countdownViewItem.RemainingTimeString, () => RemainingTimeLabel.Text);
    }
}

您在方法中将传递的视图项存储为Member变量非常重要。如果您不这样做,您只会看到初始值,绑定永远不会更新视图!

由于我们重新使用我们的细胞,我们必须假设我们的细胞可能已经被前一个项目使用,并且可能存在存在的绑定存在。

Let’s remember that Bindings boil down to events. And when we register an event handler we should always ensure that we are not creating a memory leak I.e. that we deregister the event handler. MVVM Light provides the method Detach which will deregister the event handler(s) behind the binding. Now why is this important? Well lists tend to come with multiple items. Having a memory leak for each item tends to be a bad design strategy to say the least. Therefor it is important to ensure that the binding in the cells are detached when it is no longer used.

This is also the reason why we are using a custom cell. It allows us to override the method PrepareForReuse which is invoked every time a cell is being reused. In this method we can restore the cell to it’s initial state:

public override void PrepareForReuse()
{
    base.PrepareForReuse();
    _timerBinding?.Detach();
}

And that is how you can create bindings in a UITableViewCell.

结论

In this blog post we saw how we can create a binding for a UITableViewCell. We also covered how to avoid memory leaks when reusing cells (and you should generally reuse cells). Generally do not use bindings for static content in cells as they might impact your performance. When ever possible do not change the layout (constraints) due to a change in the content. Though not supported by the UITableViewSource created by MVVM Light at the time of writing, try using different custom cells for different layouts.

您可以找到整个应用程序样本 GitHub..

Updated: