2013-09-04 2 views
7

В настоящее время я изучаю все возможные решения, чтобы иметь возможность информировать пользователя, то есть всплывать диалог, когда есть решение, которое ему нужно сделать. Это обычная проблема с шаблоном MVVM, и я пытаюсь ее решить для структуры MvvmCross.MvvmCross Dialog

Возможных решений могут быть:

  • Настройка MvxPresenter, чтобы иметь возможность показать диалоги, но это выглядит немного уродливый мне
  • Поместите диалоговый интерфейс в проекте ядра и использовать Инверсию управления для внедрить реализацию из проекта пользовательского интерфейса в основной проект
  • Используйте плагин MvxMessenger и обменивайтесь сообщениями между проектом Core и UI. Звучит неплохо, но может быть сложнее разработать ...

Что вы предложите?

+0

если ваше второе решение что-то вроде этого http://stackoverflow.com/questions/3801681/good-or-bad-practice-for-dialogs-in-wpf-with-mvvm - я поеду с тобой :) – blindmeis

ответ

13

Диалоговый ввод - интересная тема, которая не всегда хорошо сочетается с потоком передачи данных Mvvm.

Вообще, некоторые случаи использования Диалоги для таких вещей, как:

  1. добавляющих да/нет опции подтверждения к не кнопка отправки
  2. запрашивает дополнительную один вход - например, выбор из списка
  3. предлагает выбор действий (например, удалять, редактировать или дублировать?)
  4. предлагает подтверждающее сообщение
  5. запрашивающего дополнительный сложный вход - например, собирая набор firstname/lastname/age/accept_terms поле

Для некоторых из этих пунктов я бы предположил, что в основном они могут быть смоделированы как чисто взгляды. Например, запрос на выбор отдельных элементов обычно делается из составных контрольных надписей, которые отображают «сборщики» при нажатии. как MvxSpinner в https://github.com/slodge/MvvmCross-Tutorials/blob/master/ApiExamples/ApiExamples.Droid/Resources/Layout/Test_Spinner.axml#L16

Для общих случаев, когда вы хотите, чтобы общие ViewModels управляли потоком пользователя, тогда параметры, доступные в MvvmCross, включают в себя список 3, все из которых кажутся мне жизнеспособными, но я согласен с тем, что ни один из них не является совершенным.

В качестве дополнительного предложения, одно из замечательных архитектурных предложений - это команда Microsoft по шаблонам и практике. В 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, вид объекта может реализовать свойство, как:

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 -типа сам метод будет довольно просто - что-то вроде:

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, подобная реализация может быть используется - возможно, можно использовать DialogFragment и, возможно, также можно связать с помощью View в XML.

Примечание:

  • Я считаю, что основное взаимодействие может быть улучшено (по-моему), если мы добавили дополнительные IInteractionRequest<T> и InteractionRequestedEventArgs<T> определения - но, в рамки этого ответа, я держал в «основной» учет реализации так близко, как я мог к представленному в http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx
  • некоторых дополнительных вспомогательных классов также может помочь существенно упростить код вида подписки слишком
3

Как говорит Юджин, используйте плагин UserInteraction. К сожалению, нет в настоящее время реализации Windows Phone, так что вот код, который я использовал в промежуточном период:

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

Вы заметите, что реализовано не все, и даже те биты, которые ограничены (вы можете» t, например, текст кнопки «ОК»).

Конечно, мне нужно было зарегистрировать это в настройках.CS, а также:

Mvx.RegisterSingleton<IUserInteraction>(new WindowsPhoneUserInteraction()); 
Смежные вопросы