I was recently implementing some swipe to delete and edit mode in my Xamarin.iOS/MvvmCross applications. It is very common that you will have a custom TableViewDataSource that will implement and override some methods to handle this for you. It is very well documented on the Xamarin website. However I have always found myself writing the same code over and over again for custom data source. With MvvmCross we should all be about the code re-use. So I broke it down into this:
- A simple IRemove.cs interface that has one ICommand to remove an item.
- Implement this interface in ANY ViewModel
- A custom MvxStandardTableViewSource that will handle the delete overrides and takes an IRemove class.
That is is really!
Here is the interface:
public interface IRemove
{
ICommand RemoveCommand { get; }
}
The “MvxDeleteStandardTableViewSource” Impelemtnation
public class MvxDeleteStandardTableViewSource : MvxStandardTableViewSource
{
private IRemove m_ViewModel;
#region Constructors
public MvxDeleteStandardTableViewSource(IRemove viewModel, UITableView tableView, UITableViewCellStyle style, NSString cellIdentifier, IEnumerable<MvxBindingDescription> descriptions, UITableViewCellAccessory tableViewCellAccessory = 0)
: base(tableView, style, cellIdentifier, descriptions, tableViewCellAccessory)
{
m_ViewModel = viewModel;
}
public MvxDeleteStandardTableViewSource(IRemove viewModel, UITableView tableView, string bindingText) : base(tableView, bindingText)
{
m_ViewModel = viewModel;
}
public MvxDeleteStandardTableViewSource(IRemove viewModel, UITableView tableView, NSString cellIdentifier) : base(tableView, cellIdentifier)
{
m_ViewModel = viewModel;
}
public MvxDeleteStandardTableViewSource(IRemove viewModel, UITableView tableView) : base(tableView)
{
m_ViewModel = viewModel;
}
public MvxDeleteStandardTableViewSource(IRemove viewModel, UITableView tableView, UITableViewCellStyle style, NSString cellId, string binding, UITableViewCellAccessory accessory)
: base(tableView, style, cellId, binding, accessory)
{
m_ViewModel = viewModel;
}
#endregion
public override bool CanEditRow(UITableView tableView, NSIndexPath indexPath)
{
return true;
}
public override void CommitEditingStyle(UITableView tableView, UITableViewCellEditingStyle editingStyle, NSIndexPath indexPath)
{
switch (editingStyle)
{
case UITableViewCellEditingStyle.Delete:
m_ViewModel.RemoveCommand.Execute(indexPath.Row);
break;
case UITableViewCellEditingStyle.None:
break;
}
}
public override UITableViewCellEditingStyle EditingStyleForRow(UITableView tableView, NSIndexPath indexPath)
{
return UITableViewCellEditingStyle.Delete;
}
public override bool CanMoveRow(UITableView tableView, NSIndexPath indexPath)
{
return false;
}
}
Here is my implementation in my view model:
private MvxCommand<int> m_RemoveCommand;
public ICommand RemoveCommand
{
get => m_RemoveCommand ?? (m_RemoveCommand = new MvxCommand<int>(i => this.m_List.RemoveAt(i)));
}
Then in your View you will just need to setup this:
m_TableView = new UITableView();
Add(m_TableView);
var source = new MvxDeleteStandardTableViewSource (ViewModel, m_TableView, UITableViewCellStyle.Subtitle, new NSString("my_id"), "TitleText Title;DetailText Subtitle;ImageUrl Image", UITableViewCellAccessory.None);
m_TableView.Source = source;
Boom now you are getting sweet swipe to delete with ease!
I also have a full Gist on GitHub!
The possibilities are endless here as well. Your IRemove interface could also have something like a string for what to say (instead of delete) or a method that returns it based on the index. You could also apply this for the drag/drop as well, which would be pretty simple.