我正在尝试将自定义命令绑定到UIAlertController(ios 8)。我在导航栏中有一个按钮,并附有一个操作表。当用户单击导航栏按钮时,将显示操作表。当用户单击操作表按钮时,必须将其重定向到另一个视图/视图模型。
我的视图型号代码:
public ICommand AddPhonecallCommand
{
get
{
return new MvxCommand(() => ShowViewModel<AddPhonecallViewModel>();
}
}
public ICommand AddMeetingCommand
{
get
{
return new MvxCommand(() => ShowViewModel<AddMeetingViewModel>();
}
}
我的观看代码:
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);
});
因此,操作表中的每个按钮都应重定向到特定的视图模型。但似乎操作表中的按钮不是UIButton。所以我需要以某种方式将UIAlertAction绑定到ICommand。
set.Bind(...).For(...).To(vm => vm.AddPhonecallCommand);
我应该用什么来代替点?
我在视图中没有视图模型的实例。我正在使用这种语法:
var set = this.CreateBindingSet<MainView, MainViewModel>();
set.Bind(source).To(vm => vm.Events); // binding IEnumerable
set.Apply();
没有viewmodel的直接实例化。该框架为我做了所有肮脏的工作。所以,如果我正在写
set.Bind(rightNavButton).To(vm => vm.AddPhonecallCommand); // binding the Clicked event
一切正常。但如果我试着写这样的东西
var actionSheetButton = UIAlertAction.Create("Phone call", UIAlertActionStyle.Default, null));
...
set.Bind(actionSheetButton).To(vm => vm.AddPhonecallCommand); // attempt to bind
没有任何反应。可能是因为我们根本没有UIAlertAction
中的适当事件。
答案 0 :(得分:1)
如果您只想执行这些命令,那么可以使用以下简单路线:
actionSheet.AddAction(UIAlertAction.Create("Phone call", UIAlertActionStyle.Default, (s,e) => {
MyViewModel.AddPhoneCallCommand.Execute(null);
}));
如果你想要&#34;完全绑定&#34; - 包括观察视图模型更改和观察CanExecute更改,这也可以完成 - 您需要保留UIAlertAction实例并将它们的属性绑定到ViewModel。
答案 1 :(得分:0)
我找到了合适的解决方案。我为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;
}
然后我创建了一个自定义绑定类
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;
}
}
}
并修改了Setup.cs:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(
new MvxCustomBindingFactory<UIAlertActionBindable>("Click", aab => new UIAlertActionBinding(aab)));
}
视图中的代码如下所示:
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);
一切都像魅力一样。
答案 2 :(得分:0)
为什么不创建一个跨平台服务来处理这个问题?通过这种方式,您可以从视图模型中更轻松地调用窗口,并且可以在android,winphone和iOS中使用它。
public class Option {
public string Text { get; set; }
public Action Action { get; set; }
public Option(string text, Action action) {
Text = text;
Action = action;
}
}
public interface IDialogService {
void ActionSheet(string title, params Option[] option);
}
public class iOSDialogService : IDialogService {
public void ActionSheet(string title, params Option[] options) {
var sheet = UIAlertController.Create(title ?? String.Empty, String.Empty, UIAlertControllerStyle.ActionSheet);
foreach (var opt in options) {
sheet.AddAction(UIAlertAction.Create(opt.Text, UIAlertActionStyle.Default, x => {
opt.Action();
}))
);
}
private void Present(UIAlertController controller) {
this.Dispatch(() => {
var top = GetTopViewController();
var po = controller.PopoverPresentationController;
if (po != null) {
po.SourceView = top.View;
var h = (top.View.Frame.Height / 2) - 400;
var v = (top.View.Frame.Width / 2) - 300;
po.SourceRect = new RectangleF(v, h, 0, 0);
po.PermittedArrowDirections = UIPopoverArrowDirection.Any;
}
top.PresentViewController(controller, true, null);
});
}
private static UIViewController GetTopViewController() {
var root = UIApplication
.SharedApplication
.Windows
.Reverse()
.FirstOrDefault(x =>
x.WindowLevel == UIWindowLevel.Normal &&
!x.Hidden
)
.RootViewController;
var tabs = root as UITabBarController;
if (tabs != null)
return tabs.SelectedViewController;
var nav = root as UINavigationController;
if (nav != null)
return nav.VisibleViewController;
if (root.PresentedViewController != null)
return root.PresentedViewController;
return root;
}
}
现在要调用它,只需将此服务注入到视图模型中:
public IMvxCommand Choice {
get {
return new MvxCommand(() => dialogService.ActionSheet(
"Action Sheet",
new Option("Button1", () => { .. do something here }),
new Option("Button2", () => { .. do something else here })
));
}
}
要获得更完整的解决方案,请在nuget上搜索“MvvmCross用户对话框”,或者查看它here