I'm trying to bind a custom command to UIAlertController (ios 8). I have a button in the nav bar and an action sheet attached to it. When user clicks the nav bar button, the action sheet appears. When user clicks the action sheet button, he must be redirected to another view / view model.
My view model code:
public ICommand AddPhonecallCommand
{
get
{
return new MvxCommand(() => ShowViewModel<AddPhonecallViewModel>();
}
}
public ICommand AddMeetingCommand
{
get
{
return new MvxCommand(() => ShowViewModel<AddMeetingViewModel>();
}
}
My view code:
var actionSheet = UIAlertController.Create("Add a new...", null, UIAlertControllerStyle.ActionSheet);
actionSheet.AddAction(UIAlertAction.Create("Phone call", UIAlertActionStyle.Default, null));
actionSheet.AddAction(UIAlertAction.Create("Meeting", UIAlertActionStyle.Default, null));
var rightNavButton = new UIBarButtonItem("Add", UIBarButtonItemStyle.Plain, (s, e) =>
{
this.PresentViewController(actionSheet, true, null);
});
So, every button in the action sheet should redirect to a specific view model. But it seems that buttons in the action sheet are not UIButtons. So I need somehow bind UIAlertAction to ICommand.
set.Bind(...).For(...).To(vm => vm.AddPhonecallCommand);
What should I put instead of the dots?
I don’t have an instance of the view model in the view. I’m using this syntax:
var set = this.CreateBindingSet<MainView, MainViewModel>();
set.Bind(source).To(vm => vm.Events); // binding IEnumerable
set.Apply();
There is no direct instantiation of the viewmodel. The framework does all dirty work for me. So, if I'm writing
set.Bind(rightNavButton).To(vm => vm.AddPhonecallCommand); // binding the Clicked event
everything works just fine. But if I'm tryng to write something like this
var actionSheetButton = UIAlertAction.Create("Phone call", UIAlertActionStyle.Default, null));
...
set.Bind(actionSheetButton).To(vm => vm.AddPhonecallCommand); // attempt to bind
nothing happens. Probably because we don’t have suitable events in UIAlertAction
at all.
I found a suitable solution. I created a proxy class for UIAlertAction:
public class UIAlertActionBindable : UIAlertAction
{
public UIAlertAction AlertAction;
public UIAlertActionBindable(string title, UIAlertActionStyle style)
{
AlertAction = UIAlertAction.Create(title, style, action =>
{
if(Clicked != null)
{
Clicked(this, null);
}
});
}
public event EventHandler Clicked;
}
Then I created a custom binding class
public class UIAlertActionBinding : MvxTargetBinding
{
private readonly UIAlertActionBindable _view;
private IMvxCommand _command;
public UIAlertActionBinding(UIAlertActionBindable view)
: base(view)
{
_view = view;
_view.Clicked += OnClicked;
}
void OnClicked(object sender, EventArgs e)
{
if (_command != null)
{
_command.Execute();
}
}
public override void SetValue(object value)
{
_command = (IMvxCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_view.Clicked -= OnClicked;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get
{
return typeof(IMvxCommand);
}
}
public override Cirrious.MvvmCross.Binding.MvxBindingMode DefaultMode
{
get
{
return MvxBindingMode.OneWay;
}
}
}
and modified Setup.cs:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(
new MvxCustomBindingFactory<UIAlertActionBindable>("Click", aab => new UIAlertActionBinding(aab)));
}
The code in the view looks like:
var actionSheet = UIAlertController.Create("Add a new...", null, UIAlertControllerStyle.ActionSheet);
var meetingActionButton = new UIAlertActionBindable("Meeting", UIAlertActionStyle.Default);
actionSheet.AddAction(meetingActionButton.AlertAction);
...
set.Bind(meetingActionButton).For("Click").To(vm => vm.AddMeetingCommand);
Everything works like a charm.