MvvmCross 对话框 [英] MvvmCross Dialog

查看:17
本文介绍了MvvmCross 对话框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am currently investigating all possible solutions to be able to inform the user, ie pop a dialog, when there is a decision he needs to make. This is a common problem with MVVM pattern and I am trying to solve it for MvvmCross framework.

可能的解决方案是:

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

你有什么建议?

推荐答案

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

Dialog input is an interesting topic that doesn't always fit well with the flow of Mvvm Data-Binding.

通常,对话框的一些用例用于:

Generally, some use cases of Dialogs are for things like:

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

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

For some of these items, I'd suggest that mainly these could be modelled as purely View concerns. For example, requesting single item selection is commonly done from compound controls labels which display 'pickers' when tapped - e.g. like an MvxSpinner in https://github.com/slodge/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Droid/Resources/Layout/Test_Spinner.axml#L16

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

For general cases, where you want the shared ViewModels to drive the user flow, then options which are available within MvvmCross include the 3 you list, all of which seem viable to me, but I agree that none of them is perfect.

作为附加建议,Microsoft 的模式和实践团队提供了一个不错的架构建议.在 http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx,他们建议使用 IInteractionRequest 接口,该接口可用于数据绑定,尤其适用于这种情况.

As an additional suggestion, one nice architectural suggestion is from Microsoft's Pattern and Practices team. In http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx, they suggest a IInteractionRequest interface which can be used within data-binding especially for this type of situation.

他们的参考实现是:

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)));
        }
    }
}

ViewModel 的一个示例用法是:

An example ViewModel use of this is:

private InteractionRequest<Confirmation> _confirmCancelInteractionRequest = new InteractionRequest<Confirmation>();
public IInteractionRequest ConfirmCancelInteractionRequest
{
    get
    {
        return _confirmCancelInteractionRequest;
    }
}

ViewModel 可以使用:

and the ViewModel can raise this using:

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

其中 Confirmation 是一个简单的类,例如:

where Confirmation is a simple class like:

    public class Confirmation
    {
        public string Message { get; private set; }
        public bool Confirmed { get; set; }
        public Confirmation(string message)
        {
           Message = message;
        }
    }

在视图中使用它:

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

The MSDN link shows how a Xaml client might bind to this using behaviours - so I won't cover this further here.

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

In iOS for MvvmCross, a View object might implement a property like:

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);
    }
}

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

This View property uses a WeakReference-based event subscription in order to channel ViewModel Raise events through to a View MessageBox-type method. It's important to use a WeakReference so that the ViewModel never has a reference to the View - these can cause memory leak issues in Xamarin.iOS. The actual MessageBox-type method itself would be fairly simple - something like:

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();
    }; 
}

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

And the property could be bound in a Fluent Binding set like:

set.Bind(this)
   .For(v => v.ConfirmationInteraction)
   .To(vm => vm.ConfirmCancelInteractionRequest);

对于 Android,可以使用类似的实现 - 这可能使用 DialogFragment,也可能使用 XML 中的 View 进行绑定.

For Android, a similar implementation could be used - this could perhaps use a DialogFragment and could perhaps also be bound using a View within XML.

注意:

  • 我相信,如果我们进一步添加 IInteractionRequest<T>InteractionRequestedEventArgs<T> 定义,可以改进基本交互(在我看来) - 但是,对于在这个答案的范围内,我保持基本"实现尽可能接近 http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx
  • 一些额外的帮助类也有助于显着简化视图订阅代码
  • I believe that the basic interaction could be improved (in my opinion) if we added further IInteractionRequest<T> and InteractionRequestedEventArgs<T> definitions - but, for the scope of this answer, I kept to the 'basic' implementation keeping as close as I could to the one presented in http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx
  • some additional helper classes could also help significantly simplify the view subscription code too

这篇关于MvvmCross 对话框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆