Пример диалога WPF MVVM

У кого-нибудь есть примеры показа диалогового окна с использованием MVVM (Prism)? - например, окно настроек конфигурации при выполнении команды.

Все примеры, которые я видел, используют шаблон-посредник, что хорошо, но они также имеют ссылку на представление в модели представления, которое не является идеальным (мы используем DataTemplates).

Спасибо

6 ответов

Я бы использовал сервис для отображения диалога. Служба может также связать виды с моделями представления.

public interface IDialogService {
    void RegisterView<TView, TViewModel>() where TViewModel:IDialogViewModel;
    bool? ShowDialog(IDialogViewModel viewModel);
}

public interface IDialogViewModel {
    bool CanClose();
    void Close();
}


RegisterView просто связывает тип представления с типом ViewModel. Вы можете настроить эти ссылки в модуле инициализации. Это проще, чем пытаться заставить модули регистрировать шаблоны данных в верхнем слое вашего приложения.

ShowDialog Показывает модель представления, которую вы хотите отобразить. Возвращает true, false и null для закрытия, как Window.ShowDialog метод. Реализация просто создает новое представление типа TView из вашего контейнера, подключает его к предоставленной ViewModel и показывает его.

IDialogViewModel предоставляет механизм для ViewModel для проверки и отмены закрытия диалога.

У меня есть стандартное диалоговое окно с контролем содержимого в нем. когда ShowDialog вызывается, он создает новый стандартный диалог, добавляет представление к элементу управления контентом, подключает ViewModel и отображает его. Стандартный диалог уже имеет кнопки [OK] и [Отмена] с соответствующей логикой для вызова нужных методов из IDialogViewModel,

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

Затем главное окно создает экземпляр диалогового окна, передает ему модель представления и показывает диалог. Результат диалога передается вызывающей стороне в исходном сообщении.

Это выглядит примерно так:

На ваш взгляд модель:

DialogViewModel viewModel = new DialogViewModel(...);
ShowDialogMessage message = new ShowDialogMessage(viewModel);

_messenger.Broadcast(message);

if (message.Result == true)
{
    ...
}

В главном окне код позади:

void RecieveShowDialogMessage(ShowDialogMessage message)
{
    DialogWindow w = new DialogWindow();
    w.DataContext = message.ViewModel;
    message.Result = w.ShowDialog();
}

Я надеюсь, что этого достаточно, чтобы дать вам идею...

Я согласен, что использование сервиса для отображения диалогов по шаблону MVVM - самое простое решение. Но я также спросил себя, есть ли в моем проекте 3 сборки Model: ViewModel, View и в соответствии со сборкой шаблона MVVM ViewModel имеет ссылку на Model, а View - на Model и ViewModel, где я должен разместить класс DialogService? Если я помещу один в сборку ViewModel - у меня нет шансов создать экземпляр DialogView; с другой стороны, если я помещу DialogService в сборку View, как я должен внедрить его в мой класс ViewModel?

Итак, я бы рекомендовал взглянуть на сценарии Advanced MVVM с Prism Part: Использование объектов запроса взаимодействия

Как пример этого подхода:

DialogViewModelBase

public abstract class DialogViewModelBase : ViewModelBase
{
    private ICommand _ok;

    public ICommand Ok
    {
        get { return _ok ?? (_ok = new DelegateCommand(OkExecute, CanOkExecute)); }
    }

    protected virtual bool CanOkExecute()
    {
        return true;
    }

    protected virtual void OkExecute()
    {
        _isSaved = true;
        Close = true;
    }

    private ICommand _cancel;

    public ICommand Cancel
    {
        get 
        {
           return _cancel ?? (_cancel = new DelegateCommand(CancelExecute, CanCancelExecute));
        }
    }

    protected virtual bool CanCancelExecute()
    {
        return true;
    }

    protected virtual void CancelExecute()
    {
        Close = true;
    }

    private bool _isSaved = false;
    public bool IsSaved
    {
        get { return _isSaved; }
    }

    private bool _close = false;

    public bool Close
    {
        get { return _close; }
        set
        {
            _close = value;
            RaisePropertyChanged(() => Close);
        }
    }
}

CreateUserStoryViewModel:

public class CreateUserStoryViewModel : DialogViewModelBase
{
    private string _name = String.Empty;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged(() => Name);
        }
    }
}

CreateUserStoryRequest

private InteractionRequest<Notification> _createUserStoryRequest;
public InteractionRequest<Notification> CreateUserStoryRequest
{
    get
    {
        return _createUserStoryRequest ?? (_createUserStoryRequest = new InteractionRequest<Notification>());
    }
}

Команда CreateUserStory

private void CreateUserStoryExecute()
{
    CreateUserStoryRequest.Raise(new Notification()
    {
        Content = new CreateUserStoryViewModel(),
        Title = "Create User Story"
    }, 
    notification =>
                 {
                      CreateUserStoryViewModel createUserStoryViewModel =
                               (CreateUserStoryViewModel)notification.Content;
                      if (createUserStoryViewModel.IsSaved)
                      {
                         _domainContext.CreateUserStory(
new UserStory(){ Name = createUserStoryViewModel.Name, });
                      }
                 });
}

XAML:

<!--where xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
          xmlns:ir="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"-->

<i:Interaction.Triggers>
  <ir:InteractionRequestTrigger SourceObject="{Binding CreateUserStoryRequest}">
    <ir:PopupChildWindowAction>
      <ir:PopupChildWindowAction.ChildWindow>
        <view:CreateUserStory />
      </ir:PopupChildWindowAction.ChildWindow>
    </ir:PopupChildWindowAction>
  </ir:InteractionRequestTrigger>
</i:Interaction.Triggers>

Как я понял из вашего комментария выше, вопрос заключается не столько в показе диалогов, сколько в их скрытии. Есть два способа решения этой проблемы:

  1. Используйте стандартное диалоговое окно для реализации представления. Это потребует наличия слабо связанного способа связи между View и ViewModel, чтобы ViewModel мог уведомить View о том, что можно закрыться, не имея ссылки на представление.

    Существует несколько фреймворков, которые позволят это сделать - агрегаторы событий Prism будут одним из них. В этом сценарии View будет подписываться на событие (скажем, MyDialogResultValidated), а при получении события он установит DialogResult с одобрением. ViewModel (в его SaveCommand) будет запускать событие, если проверка прошла успешно.

  2. Не используйте стандартное диалоговое окно для реализации представления. Это потребовало бы наложения, которое бы эффективно имитировало модальность.

    В этом сценарии видимость представления и наложения будет привязана к свойству IsVisible ViewModel, которое будет установлено соответствующим образом реализацией SaveCommand или всякий раз, когда ViewModel необходимо показать представление.

Первый подход потребует наличия небольшого количества кода в выделенном коде, требует добавления глобальных событий и (возможно) меньше MVVM-иша. Второй подход потребует реализации (или с использованием чужого реализации) накладываемого, но не требуют ни кода в коде-позади, не будет требовать, имеющих глобальное событие (ы), и (спорно) более MVVM иш,

Это не Prism, но эта демоверсия MVVM имеет диалог настроек, который полностью MVVM.

Вы можете быть заинтересованы в следующем примере приложения:

http://compositeextensions.codeplex.com/

Он использует Prism2 с шаблоном PresentationModel (он же MVVM). Пример приложения содержит модальное диалоговое окно.

Другие вопросы по тегам