You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在MvvmCross中为UITableView单元格内按钮绑定命令的正确方法

正确实现MvvmCross中UITableViewCell按钮导航到详情页的方案

Hey there! Let's break down how to get this working properly in MvvmCross, starting with a quick look at your proposed approaches, then landing on the cleanest, most MVVM-aligned solution.

First, let's evaluate your four ideas:

  • Idea 1: Expose button outlet and bind in GetOrCreateCellFor
    This works, but it shoves binding logic into the TableViewSource—breaking MVVM's separation of concerns. View-layer code shouldn't handle binding setup, which makes maintenance harder as your app scales. Not recommended.

  • Idea 2: Pass ShowItemDetailsCommand to each cell and bind
    This is the best MVVM-compliant approach! It leverages MvvmCross's binding system to keep logic in the ViewModel, where it belongs. We'll build out this approach below.

  • Idea 3: Simple callback from cell to view
    This falls back to traditional iOS delegate patterns, ditching the benefits of MvvmCross's binding. It creates tight coupling between your cell and view, making testing and refactoring harder. Skip this.

  • Idea 4: Individual ViewModels per cell
    Overkill for this use case. Your cells only need to display data and trigger a navigation action—no complex independent logic. Adding a ViewModel per cell would add unnecessary complexity.


Step-by-Step Implementation

Let's modify your code to implement the recommended approach (Idea 2, refined):

1. Update the ItemsViewModel to use a parameterized command

We need a command that accepts an Item parameter to know which detail page to navigate to:

public class Item { public string Name { get; set; } }

public class ItemsViewModel : MvxViewModel 
{ 
    public List<Item> Items { get; } 
    // Change to MvxCommand<Item> to accept the selected item
    public MvxCommand<Item> ShowItemDetailsCommand { get; } 
    readonly IMvxNavigationService _navigationService; 
    readonly IDatabaseService _databaseService; 

    public ItemsViewModel(IMvxNavigationService navigationService, IDatabaseService databaseService) 
    { 
        ShowItemDetailsCommand = new MvxCommand<Item>(ShowItemDetails); 
        _navigationService = navigationService; 
        _databaseService = databaseService; 
        Items = _databaseService.SelectItems(); 
    } 

    // Now accepts the Item parameter directly
    async void ShowItemDetails(Item item) 
    { 
        await _navigationService.Navigate<ItemDetailsViewModel, Item>(item); 
    } 
}

2. Update the ItemsView to configure cell bindings

We'll set up the binding for the cell's button directly in the view's binding set, keeping the cell decoupled from the parent ViewModel:

public partial class ItemsView : MvxTableViewController<ItemsViewModel> 
{ 
    public ItemsView() : base("ItemsView", null) {} 

    public override void ViewDidLoad() 
    { 
        base.ViewDidLoad(); 
        TableView = View as UITableView; 

        var source = new MvxTableViewSource(TableView)
        {
            ItemTemplateSelector = new MvxDefaultTemplateSelector(ItemCellView.kCellId)
        };

        var bindings = this.CreateBindingSet<ItemsView, ItemsViewModel>(); 
        // Bind the items collection to the table source
        bindings.Bind(source).To(vm => vm.Items);
        // Configure bindings for each cell's controls
        bindings.Bind(source)
            .For(s => s.ItemTemplateBindings)
            .To(vm => new
            {
                // Bind the name label to the Item's Name property
                Name = new { Path = "Name" },
                // Bind the button's command to the ViewModel's command, passing the current Item as parameter
                DetailButton = new
                {
                    Command = vm.ShowItemDetailsCommand,
                    CommandParameter = "$dataContext" // "$dataContext" refers to the current Item in the cell
                }
            });
        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; 
        } 
    } 
}

3. Simplify the ItemCellView

Since we're handling all bindings in the parent view, we can keep the cell clean (or retain cell-internal bindings if you prefer):

public partial class ItemCellView : MvxTableViewCell 
{ 
    public const string kCellId = "item_cell"; 
    // [Outlet] UIButton DetailButton exists in the .designer.cs file

    public ItemCellView(IntPtr handle) : base(handle) 
    {
        // Optional: If you prefer cell-internal bindings, uncomment this block
        // this.DelayBind(() => 
        // { 
        //     var itemsViewModel = (this.ViewController as ItemsView).ViewModel;
        //     var bindings = this.CreateBindingSet<ItemCellView, Item>(); 
        //     bindings.Bind(Name).To(i => i.Name);
        //     bindings.Bind(DetailButton)
        //         .For(b => b.Command)
        //         .To(() => itemsViewModel.ShowItemDetailsCommand);
        //     bindings.Bind(DetailButton)
        //         .For(b => b.CommandParameter)
        //         .To(i => i);
        //     bindings.Apply(); 
        // }); 
    } 
}

Key Notes

  • Using MvxCommand<Item> directly solves your original question of how to pass the selected item to the navigation logic.
  • The $dataContext binding path refers to the current item bound to the cell, ensuring we pass the correct item to the command.
  • This approach keeps all business logic (navigation) in the ViewModel, making it testable and separated from UI code—true to MVVM principles.

内容的提问来源于stack exchange,提问作者Ivan Mir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:39:29