【问题标题】:The proper way to bind a command to a button inside a table cell in MvvmCross将命令绑定到 MvvmCross 中表格单元格内的按钮的正确方法
【发布时间】:2018-05-22 16:46:53
【问题描述】:

我正在尝试设置一个项目表 (UITableView),每个单元格中都有一个按钮,可以导航到其项目的详细配置文件。

但我不确定 MvvmCross 中的正确方法是什么。我的一些想法:

  • ItemCellView的按钮出口公开为公共,并将其绑定在GetOrCreateCellFor
  • ShowItemDetailsCommand 传递给每个ItemCellView 并将其绑定到那里
  • 使用从ItemCellViewItemsView 的简单回调而不是绑定
  • 为每个单元格获取一个单独的MvxViewModel 并从那里调用导航服务
public class Item
{
    public string Name { get; set; }
}
public class ItemsViewModel : MvxViewModel
{
    public List<Item> Items { get; }
    public MvxCommand ShowItemDetailsCommand { get; }

    readonly IMvxNavigationService _navigationService;
    readonly IDatabaseService _databaseService;

    public ItemsViewModel(IMvxNavigationService navigationService, IDatabaseService databaseService)
    {
        ShowItemDetailsCommand = new MvxCommand(ShowItemDetails);

        _navigationService = navigationService;
        _databaseService = databaseService;

        Items = _databaseService.SelectItems();
    }

    void ShowItemDetails()
    {
        // not sure how "item" gets here so far
        _navigationService.Navigate<ItemDetailsViewModel, Item>(item);
    }
}
public partial class ItemsView : MvxTableViewController<ItemsViewModel>
{
    public ItemsView() : base("ItemsView", null) {}

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

        TableView = View as UITableView;

        var source = new TableViewSource(TableView);
        var bindings = this.CreateBindingSet<ItemsView, ItemsViewModel>();
        bindings.Bind(source).To(vm => vm.Items);
        bindings.Apply();

        TableView.Source = source;
        TableView.ReloadData();
    }

    public class TableViewSource : MvxTableViewSource
    {
        public TableViewSource(UITableView tableView) : base(tableView)
        {
            TableView.RegisterNibForCellReuse(UINib.FromName("ItemCellView", NSBundle.MainBundle), ItemCellView.kCellId);
        }

        protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
        {
            return TableView.DequeueReusableCell(ItemCellView.kCellId, indexPath) as ItemCellView;
        }
    }
}
public partial class ItemCellView : MvxTableViewCell
{
    public const string kCellId = "item_cell";
    // also has an [Outlet] UIButton in the .designer.cs part

    public ItemCellView(IntPtr handle) : base(handle)
    {
        this.DelayBind(() =>
        {
            var bindings = this.CreateBindingSet<ItemCellView, Item>();
            bindings.Bind(Name).To(i => i.Name);
            bindings.Apply();
        });
    }
}

【问题讨论】:

    标签: xamarin.ios mvvmcross


    【解决方案1】:

    您应该在单元格的构造方法中绑定ItemCellView 的按钮:

    // MyBtn is my Cell's button outlet
    protected ItemCellView(IntPtr handle) : base(handle)
    {
        this.DelayBind(() =>
        {
            var bindings = this.CreateBindingSet<MyTableViewCell, Item>();
    
            // Use this bind to set your button's title
            bindings.Bind(MyBtn).For("Title").To(item => item.Name);
    
            // This bind is used for binding a command in the Item model
            // CommandParameter can pass your parameter
            bindings.Bind(MyBtn).To(item => item.ShowItemDetailsCommand).CommandParameter(DataContext);
    
            bindings.Apply();
        });
    }
    

    由于 Cell 中的 DataContext 已更改为您的 Item 模型,因此应在模型类中配置绑定命令:

    public class Item
    {
        private readonly Lazy<IMvxNavigationService> _navigationService = new Lazy<IMvxNavigationService>(Mvx.Resolve<IMvxNavigationService>);
    
        public string Name { set; get; }
    
        private ICommand showItemDetailsCommand;
        public ICommand ShowItemDetailsCommand
        {
            get
            {
                return showItemDetailsCommand ?? (showItemDetailsCommand = new MvxCommand<Item>(ShowItemDetails));
            }
        }
        async void ShowItemDetails(Item item)
        {
            await _navigationService.Value.Navigate<SecondViewModel, Item>(item);
        }
    }
    

    最后你要推送的SecondViewModel会通过事件Prepare()接收到这个参数:

    public class SecondViewModel : MvxViewModel<Item>
    {
        public override void Prepare(Item parameter)
        {
    
        }
    }
    

    【讨论】:

    • 谢谢!我可能应该制作一个 ItemCellViewModel 包装器,因为在我的情况下,Item 是由数据库层创建的纯模型类。
    • 另一方面,如果可以使用整个单元格来执行此操作,那么表源的just binding SelectionChangedCommand 就足够了。
    • 是的,如果您没有特殊要求使用按钮实现推送。 SelectionChangedCommand 这样做会更好。