UVlertController绑定在mvvmcross中

时间:2014-10-23 17:33:46

标签: ios binding mvvmcross

我正在尝试将自定义命令绑定到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中的适当事件。

3 个答案:

答案 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