9

私は現在、ユーザーが決定を下す必要があるときにダイアログを表示するなど、ユーザーに通知できるようにするためのすべての可能な解決策を調査しています。これは MVVM パターンの一般的な問題であり、MvvmCross フレームワークで解決しようとしています。

考えられる解決策は次のとおりです。

  • ダイアログを表示できるように MvxPresenter をカスタマイズしますが、それは私には少し見苦しく見えます
  • Core プロジェクトに Dialog インターフェイスを配置し、Inversion of Control を使用して UI プロジェクトから Core プロジェクトに実装を注入します。
  • MvxMessenger プラグインを使用して、Core プロジェクトと UI プロジェクトの間でメッセージを共有します。良いアイデアのように思えますが、開発するのはもっと複雑かもしれません...

何を提案しますか?

4

3 に答える 3

15

ダイアログ入力は興味深いトピックであり、Mvvm Data-Binding のフローに必ずしも適合するとは限りません。

一般に、ダイアログのいくつかの使用例は次のようなものです。

  1. 送信ボタンに yes/no 確認オプションを追加する
  2. 追加の単一入力の要求 - リストからの選択など
  3. アクションの選択肢を提供する (例: 削除、編集、または複製?)
  4. 確認メッセージの提供
  5. 追加の複雑な入力の要求 - 例: firstname/lastname/age/accept_terms フィールドのセットの収集

これらの項目のいくつかについては、主にこれらを純粋なビューの懸念としてモデル化することをお勧めします。たとえば、単一のアイテムの選択を要求することは、一般的に、タップすると「ピッカー」を表示する複合コントロール ラベルから行われます。 /Resources/Layout/Test_Spinner.axml#L16

共有 ViewModel でユーザー フローを駆動する一般的なケースでは、MvvmCross 内で使用できるオプションには、リストした 3 つが含まれます。これらはすべて実行可能に思えますが、どれも完璧ではないことに同意します。

追加の提案として、Microsoft の Pattern and Practices チームからの優れたアーキテクチャの提案が 1 つあります。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)));
        }
    }
}

これの ViewModel の使用例は次のとおりです。

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

ViewModel は次を使用してこれを発生させることができます。

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

Confirmation次のような単純なクラスです。

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

ビューでこれを使用するには:

MSDN リンクは、Xaml クライアントが動作を使用してこれにバインドする方法を示しているため、ここではこれ以上説明しません。

MvvmCross の iOS では、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);
    }
}

この View プロパティは、WeakReferenceViewModelRaiseイベントを ViewMessageBoxタイプのメソッドに渡すために、 ベースのイベント サブスクリプションを使用します。WeakReferenceViewModel が を参照しないように を使用することが重要です。これViewにより、Xamarin.iOS でメモリ リークの問題が発生する可能性があります。実際のMessageBox-type メソッド自体はかなり単純です - 次のようになります:

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 セットでバインドできます。

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

Android の場合、同様の実装を使用できます。これはおそらく を使用でき、 XML 内で DialogFragmentを使用してバインドすることもできます。View

ノート:

  • IInteractionRequest<T>さらに定義を追加すれば、基本的な相互作用を改善できると思います(私の意見では)が、この回答の範囲では、 httpInteractionRequestedEventArgs<T>で提示されたものにできるだけ近づけて「基本的な」実装を維持しました://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx
  • いくつかの追加のヘルパー クラスも、ビュー サブスクリプション コードを大幅に簡素化するのに役立ちます。
于 2013-09-04T21:58:37.210 に答える
7

Brian Chance のMvvmCross UserInteraction プラグインを使用できます

于 2013-10-26T06:04:11.503 に答える
3

As Eugene says, use the UserInteraction plugin. Unfortunately, there's not currently a Windows Phone implementation, so here's the code I've used in the interim:

public class WindowsPhoneUserInteraction : IUserInteraction
{
    public void Confirm(string message, Action okClicked, string title = null, string okButton = "OK", string cancelButton = "Cancel")
    {
        Confirm(message, confirmed =>
        {
            if (confirmed)
                okClicked();
        },
        title, okButton, cancelButton);
    }

    public void Confirm(string message, Action<bool> answer, string title = null, string okButton = "OK", string cancelButton = "Cancel")
    {
        var mbResult = MessageBox.Show(message, title, MessageBoxButton.OKCancel);
        if (answer != null)
            answer(mbResult == MessageBoxResult.OK);
    }

    public Task<bool> ConfirmAsync(string message, string title = "", string okButton = "OK", string cancelButton = "Cancel")
    {
        var tcs = new TaskCompletionSource<bool>();
        Confirm(message, tcs.SetResult, title, okButton, cancelButton);
        return tcs.Task;
    }

    public void Alert(string message, Action done = null, string title = "", string okButton = "OK")
    {
        MessageBox.Show(message, title, MessageBoxButton.OK);
        if (done != null)
            done();
    }

    public Task AlertAsync(string message, string title = "", string okButton = "OK")
    {
        var tcs = new TaskCompletionSource<object>();
        Alert(message, () => tcs.SetResult(null), title, okButton);
        return tcs.Task;
    }

    public void Input(string message, Action<string> okClicked, string placeholder = null, string title = null, string okButton = "OK", string cancelButton = "Cancel", string initialText = null)
    {
        throw new NotImplementedException();
    }

    public void Input(string message, Action<bool, string> answer, string placeholder = null, string title = null, string okButton = "OK", string cancelButton = "Cancel", string initialText = null)
    {
        throw new NotImplementedException();
    }

    public Task<InputResponse> InputAsync(string message, string placeholder = null, string title = null, string okButton = "OK", string cancelButton = "Cancel", string initialText = null)
    {
        throw new NotImplementedException();
    }

    public void ConfirmThreeButtons(string message, Action<ConfirmThreeButtonsResponse> answer, string title = null, string positive = "Yes", string negative = "No", string neutral = "Maybe")
    {
        throw new NotImplementedException();
    }

    public Task<ConfirmThreeButtonsResponse> ConfirmThreeButtonsAsync(string message, string title = null, string positive = "Yes", string negative = "No", string neutral = "Maybe")
    {
        throw new NotImplementedException();
    }
}

You'll notice that not everything's implemented, and even those bits that are are limited (you can't set the OK ad Cancel button text, for example)

Of course, I needed to register this in setup.cs as well:

Mvx.RegisterSingleton<IUserInteraction>(new WindowsPhoneUserInteraction());
于 2014-03-14T06:58:46.660 に答える