MvvmCross对话

Fab*_*ien 9 dialog modal-dialog mvvm mvvmcross

我正在调查所有可能的解决方案,以便能够在需要做出决定时通知用户,即弹出对话框.这是MVVM模式的常见问题,我正在尝试为MvvmCross框架解决它.

可能的解决办法是:

  • 自定义MvxPresenter以便能够显示对话框,但这看起来有点难看
  • 在Core项目中放置一个Dialog接口,并使用Inversion of Control将实现从UI项目注入到Core项目中
  • 使用MvxMessenger插件并在Core和UI项目之间共享消息.听起来不错,但开发起来可能更复杂......

你会建议什么?

Stu*_*art 14

对话输入是一个有趣的主题,并不总是适合Mvvm数据绑定的流程.

通常,Dialog的一些用例适用于以下内容:

  1. 在提交按钮中添加"是/否"确认选项
  2. 请求额外的单个输入 - 例如从列表中选择
  3. 提供行动选择(例如删除,编辑或复制?)
  4. 提供确认信息
  5. 请求额外的复杂输入 - 例如收集一组firstname/lastname/age/accept_terms字段

对于其中一些项目,我建议主要将这些项目建模为纯粹的View关注点.例如,请求单个项目选择通常来自复合控件标签,这些标签在点击时显示"选择器" - 例如,如https://github.com/slodge/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Droid中的MvxSpinner /Resources/Layout/Test_Spinner.axml#L16

对于一般情况,你希望共享的ViewModel驱动用户流,那么MvvmCross中可用的选项包括你列出的3个,所有这些对我来说都是可行的,但我同意它们都不是完美的.

作为一个额外的建议,一个很好的架构建议来自微软的模式和实践团队.在http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx中,他们提出了一个IInteractionRequest可以在数据绑定中使用的接口,特别是对于这种情况.

他们的参考实现是:

public interface IInteractionRequest
{
    event EventHandler<InteractionRequestedEventArgs> Raised;
}

    public class InteractionRequestedEventArgs : EventArgs
    {
       public Action Callback { get; private set; }
       public object Context { get; private set; }
       public InteractionRequestedEventArgs(object context, Action callback)
       {
           Context = context;
           Callback = callback;
       }
    }

public class InteractionRequest<T> : IInteractionRequest
{
    public event EventHandler<InteractionRequestedEventArgs> Raised;

    public void Raise(T context, Action<T> callback)
    {
        var handler = this.Raised;
        if (handler != null)
        {
            handler(
                this, 
                new InteractionRequestedEventArgs(
                    context, 
                    () => callback(context)));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ViewModel使用的示例是:

private InteractionRequest<Confirmation> _confirmCancelInteractionRequest = new InteractionRequest<Confirmation>();
public IInteractionRequest ConfirmCancelInteractionRequest
{
    get
    {
        return _confirmCancelInteractionRequest;
    }
}
Run Code Online (Sandbox Code Playgroud)

并且ViewModel可以使用以下方法来提升:

_confirmCancelInteractionRequest.Raise(
    new Confirmation("Are you sure you wish to cancel?"),
    confirmation =>
    {
        if (confirmation.Confirmed)
        {
            this.NavigateToQuestionnaireList();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

哪个Confirmation是简单的类,如:

    public class Confirmation
    {
        public string Message { get; private set; }
        public bool Confirmed { get; set; }
        public Confirmation(string message)
        {
           Message = message;
        }
    }
Run Code Online (Sandbox Code Playgroud)

在视图中使用它:

MSDN链接显示了Xaml客户端如何使用行为绑定到此 - 所以我不会在这里进一步讨论.

在iOS for MvvmCross中,View对象可能实现如下属性:

private MvxGeneralEventSubscription _confirmationSubscription;
private IInteractionRequest _confirmationInteraction;
public IInteractionRequest ConfirmationInteraction
{
    get { return _confirmationInteraction; }
    set
    {
        if (_confirmationInteraction == value)
            return;
        if (_confirmationSubscription != null)
            _confirmationSubscription.Dispose();
        _confirmationInteraction = value;
        if (_confirmationInteraction != null)
            _confirmationSubscription = _confirmationInteraction
                .GetType()
                .GetEvent("Raised")
                .WeakSubscribe(_confirmationInteraction, 
                   DoConfirmation);
    }
}
Run Code Online (Sandbox Code Playgroud)

此View属性使用WeakReference基于事件的订阅,以便将ViewModel Raise事件引导到View MessageBox-type方法.使用一个是非常重要的,WeakReference以便ViewModel永远不会引用View- 这些可能会导致Xamarin.iOS中的内存泄漏问题.实际的MessageBox类型方法本身很简单 - 类似于:

private void DoConfirmation(InteractionRequestedEventArgs args)
{
    var confirmation = (Confirmation)args.Context;

    var alert = new UIAlertView(); 
    alert.Title = "Bazinga"; 
    alert.Message = confirmation.Message; 

    alert.AddButton("Yes"); 
    alert.AddButton("No"); 

    alert.Clicked += (sender, e) => { 
       var alertView = sender as UIAlertView; 

       if (e.ButtonIndex == 0) 
       { 
          // YES button
          confirmation.Confirmed = true;
       } 
       else if (e.ButtonIndex == 1) 
       { 
          // NO button
          confirmation.Confirmed = false; 
       } 

       args.Callback();
    }; 
}
Run Code Online (Sandbox Code Playgroud)

该属性可以绑定在Fluent Binding集中,如:

set.Bind(this)
   .For(v => v.ConfirmationInteraction)
   .To(vm => vm.ConfirmCancelInteractionRequest);
Run Code Online (Sandbox Code Playgroud)

对于Android,可以使用类似的实现 - 这可能使用a DialogFragment,也许也可以使用ViewXML内部绑定.

注意:

  • 我相信如果我们进一步添加IInteractionRequest<T>InteractionRequestedEventArgs<T>定义,基本的交互可以改进(在我看来)- 但是,对于这个答案的范围,我保持'基本'实现保持尽可能接近http中呈现的那个://msdn.microsoft.com/en-us/library/gg405494(v = pandp.40)的.aspx
  • 一些额外的帮助程序类也可以帮助显着简化视图订阅代码


Eug*_*kov 7

你可以使用Brian Chance的MvvmCross UserInteraction插件