2013-06-11 2 views
5

Я использую MvvmCross для создания моего Android-приложение, и я столкнулся со следующей проблемой:Показать AlertDialog из ViewModel с помощью MvvmCross

Когда я пытаюсь показать AlertDialog, который был создан в ViewModel, то

"Необработанное исключение: появляется Android.Views.WindowManagerBadTokenException".

public class MyViewModel : MvxViewModel 
{ 
    public ICommand ShowAlertCommand { get; private set; } 

    public AuthorizationViewModel() 
    { 
     ShowAlertCommand = new MvxCommand(() => 
      { 
       var adb = new AlertDialog.Builder(Application.Context); 
       adb.SetTitle("Title here"); 
       adb.SetMessage("Message here"); 
       adb.SetIcon(Resource.Drawable.Icon); 
       adb.SetPositiveButton("OK", (sender, args) => { /* some logic */}); 
       adb.SetNegativeButton("Cancel", (sender, args) => { /* close alertDialog */}); 

       adb.Create().Show(); 
      }); 
    } 
} 

Когда я researching я обнаружил, что это происходит из-за передачи ссылки на контекст, но не на активность в AlertDialog.Builder.

В this topic я нашел следующее решение: Получить ссылки на текущую активность за счет использования GetService(), но я не нашел mvvmcross плагинов для работы с IMvxServiceConsumer, IMvxAndroidCurrentTopActivity интерфейсы.

Мой вопрос Можно ли показать AlertDialog с ViewModel? И как я могу получить ссылку на Activity, но не на Application.Context? И какой правильный способ закрыть AlertDialog, чтобы пользователь оставался в текущем представлении?

ответ

11

В общем, вы должны стараться не ставить этот тип кода в ViewModels

  • потому что ViewModels должны оставаться независимо от платформы
  • потому ViewModels должна быть единица проверяемым - и это трудно для модульного тестирования, когда код показывает диалог

Я бы также рекомендовал, чтобы вы не помещали такой код внутри конструктора ViewModel - эти конструкторы, как правило, вызывается во время навигации, а отображение диалога во время перехода, вероятно, будет проблемой матический.


С этими вещами сказал, если вы хотите, чтобы разжиться текущей верхней деятельность в любом коде, то вы можете сделать это с помощью IMvxAndroidCurrentTopActivity

public interface IMvxAndroidCurrentTopActivity 
{ 
    Activity Activity { get; } 
} 

Используя это, любой код может получить ток Activity с помощью:

var top = Mvx.Resolve<IMvxAndroidCurrentTopActivity>(); 
var act = top.Activity; 
if (act == null) 
{ 
    // this can happen during transitions 
    // - you need to be sure that this won't happen for your code 
    throw new MvxException("Cannot get current top activity"); 
} 

var dlg = new AlertDialog.Builder(act); 
//... 
dlg.Create().Show(); 

использование IMvxAndroidCurrentTopActivity обсуждается в MvvmCross: How to pass Android context down to MvxCommand?

подход, примененный в этом вопросе/ответ также является одним из способов, я обычно приближающихся показывать диалоги из ViewModel:

  • я бы создал IFooDialog интерфейс
  • В идеале I вероятно, сделает этот интерфейс асинхронным - напримерс помощью async или с помощью параметра Action<DialogResult> обратного вызова
  • на каждой платформе, я бы реализовать, что в проекте UI
  • в ViewModel s может затем использовать IFooDialog когда диалог необходим, и каждая платформа может реагировать с соответствующим действием UI

Этот подход типа «Dialog Service» распространен в Mvvm - например, см. статьи http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern (хотя эта статья очень специфична для Windows!)

Здесь также есть несколько других вопросов о MvvmCross и диалогах, хотя они могут содержать ссылку на старый код v1 или vNext. Alerts or Popups in MvvmCross и Unable run ProgressDialog - BadTokenException while showind

+0

Хорошо, я пробую этот материал :) –

+0

Я согласен с тем, что работа с пользовательским интерфейсом от VM не является «фэн-шуй»;), но платформы WinPhone и iPhone уже запущены, и мне просто нужно было в Android версию моего приложения. В будущем, в моих новых проектах, я обязательно буду развиваться согласно канонам MVVM :) –

+0

Стюарт, короткий вопрос о 'IMvxAndroidCurrentTopActivity': будет ли он всегда быть тем же самым экземпляром, даже если текущая верхняя активность изменяется? Предпосылки: я не являюсь поклонником местоположения службы, вместо этого мои классы принимают явные зависимости. Будет ли это работать, если у моего одноэлементного класса есть зависимость конструктора от 'IMvxAndroidCurrentTopActivity' и он все еще может работать с текущей деятельностью? –