Как вы успешно реализовали функциональность MessageBox.Show() в MVVM?
-
11-09-2019 - |
Вопрос
У меня есть приложение WPF который вызывает MessageBox.Show() еще во ViewModel (чтобы проверить, действительно ли пользователь хочет удалить). Это действительно работает, но идет вразрез с принципом MVVM поскольку ViewModel не должна явно определять, что происходит в представлении.
Итак, теперь я думаю как мне лучше всего реализовать функциональность MessageBox.Show() в моем приложении MVVM параметры:
Я мог бы получить сообщение с текстом "Вы уверены ...?" Наряду с двумя кнопками «да» и «нет» на границе в моем XAML, и создать триггер на шаблоне, чтобы он был рухну AreYourSureDialogueBoxIsVisible, а затем, когда мне понадобится это диалоговое окно, присвойте AreYourSureDialogueBoxIsVisible значение «true», а также обработайте две кнопки через DelegateCommand обратно в мою ViewModel.
Я также мог бы каким-то образом попытаться обработать это с помощью триггеров в XAML, чтобы кнопка «Удалить» фактически просто вызывала появление некоторого элемента Border с сообщением и кнопками в нем, а кнопка «Да» фактически выполняла удаление.
Оба решения кажутся слишком сложными для того, что раньше было парой строк кода с MessageBox.Show().
Каким образом вы успешно реализовали диалоговые окна в своих приложениях MVVM?
Решение
Из двух упомянутых вами вариантов я предпочитаю вариант №2.Кнопка «Удалить» на странице просто вызывает появление «Диалог подтверждения удаления».«Диалог подтверждения удаления» фактически запускает удаление.
Вы проверяли Карла Шиффлетта? Слайды и демонстрации направления бизнеса WPF?Я знаю, что он делает что-то подобное.Попробую вспомнить где.
РЕДАКТИРОВАТЬ:Ознакомьтесь с демонстрацией № 11 «Проверка данных в MVVM» (EditContactItemsControlSelectionViewModel.DeleteCommand).Карл вызывает всплывающее окно из ViewModal (Что!?:-).Мне вообще-то твоя идея больше нравится.Кажется, проще провести модульное тестирование.
Другие советы
Службы спасения.С использованием Оникс (отказ от ответственности, я автор) это так же просто, как:
public void Foo()
{
IDisplayMessage dm = this.View.GetService<IDisplayMessage>();
dm.Show("Hello, world!");
}
В запущенном приложении это косвенно вызовет MessageBox.Show("Hello, world!").При тестировании сервис IDisplayMessage можно имитировать и предоставлять ViewModel, чтобы он мог делать все, что вы хотите выполнить во время теста.
Чтобы расширить ответ Дина Чока теперь, когда его ссылка окончена:
В файле App.xaml.cs мы подключаем диалоговое окно подтверждения к модели представления.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var confirm = (Func<string, string, bool>)((msg, capt) => MessageBox.Show(msg, capt, MessageBoxButton.YesNo) == MessageBoxResult.Yes);
var window = new MainWindowView();
var viewModel = new MainWindowViewModel(confirm);
window.DataContext = viewModel;
...
}
В представлении (MainWindowView.xaml) у нас есть кнопка, вызывающая команду во ViewModel.
<Button Command="{Binding Path=DeleteCommand}" />
ViewModel (mainwindowviewmodel.cs) использует команду делегата, чтобы показать "вы уверены?" диалог и выполните действие.В данном примере это SimpleCommand
похожий на этот, но подойдет любая реализация ICommand.
private readonly Func<string, string, bool> _confirm;
//constructor
public MainWindowViewModel(Func<string, string, bool> confirm)
{
_confirm = confirm;
...
}
#region Delete Command
private SimpleCommand _deleteCommand;
public ICommand DeleteCommand
{
get { return _deleteCommand ?? (_deleteCommand = new SimpleCommand(ExecuteDeleteCommand, CanExecuteDeleteCommand)); }
}
public bool CanExecuteDeleteCommand()
{
//put your logic here whether to allow deletes
return true;
}
public void ExecuteDeleteCommand()
{
bool doDelete =_confirm("Are you sure?", "Confirm Delete");
if (doDelete)
{
//delete from database
...
}
}
#endregion
Я просто создаю интерфейс (IMessageDisplay или аналогичный), который внедряется в виртуальную машину, и у него есть такие методы, как MessageBox (ShowMessage() и т. д.).Вы можете реализовать это, используя стандартное окно сообщений или что-то более специфичное для WPF (я использую это на CodePlex какой-то парень по имени Праджиш).
Таким образом, все разделено и тестируемо.
А как насчет создания такого события, как "MessageBoxRequested"
обрабатывается в выделенном коде представления (в любом случае это код только для просмотра, поэтому я не вижу никаких проблем с наличием этого кода в выделенном коде).
Я создал простой элемент управления-оболочки MessageBox, который мы можем использовать в чистом решении MVVM, при этом сохраняя возможность модульного тестирования.Подробности в моем блоге http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
мукапу
Я реализовал поведение, которое прослушивает сообщение из ViewModel.Он основан на решении Лорана Бюньона, но, поскольку он не использует код и его можно использовать повторно, я думаю, что он более элегантен.
На всякий случай, если кто-то еще читает и недоволен:
Я просто хотел обработать MessageBoxes типа «уведомление» (т.меня не волнует DialogResult
), но проблема с большинством решений, о которых я читал, заключается в том, что они, похоже, косвенно заставляют вас выбирать реализацию View (то есть в настоящее время у меня есть MessageBox.Show
, но если позже я решу просто поиграть с видимостью скрытой панели непосредственно в моем представлении, это не будет очень хорошо сочетаться с INotification
интерфейс, переданный в ViewModel).
Поэтому я пошел быстро и грязно:
ViewModel имеет string NotificationMessage
имущество, об изменениях уведомлено PropertyChanged
.
Представление подписывается на PropertyChanged
, и если он увидит NotificationMessage
собственность проходит, делает все, что хочет.
Хорошо, это означает, что представление имеет код программной части и имя PropertyChanged
жестко запрограммировано, но в любом случае оно будет жестко запрограммировано в XAML.И это означает, что я избегаю таких вещей, как конвертеры видимости и свойства, позволяющие определить, видимо ли уведомление по-прежнему или нет.
(По общему признанию, это только для ограниченного варианта использования (выстрелил и забыл), я не особо задумывался о том, как можно его расширить.)
Я бы просто скинул с ВМ.Я не хочу использовать чужой сервис или писать свой собственный только для того, чтобы отправить сообщение.
Недавно я столкнулся с этой проблемой, когда мне пришлось заменить MessageBox.Show в ViewModels каким-то полностью механизмом окна сообщений о жалобах MVVM.
Чтобы добиться этого, я использовал InteractionRequest<Notification>
и InteractionRequest<Confirmation>
вместе с триггерами взаимодействия и написал свои собственные представления для окна сообщений.
То, что я реализовал, опубликовано здесь
На эту тему существует так много ответов, которые варьируются от создания собственного класса до использования сторонних библиотек.Я бы посоветовал использовать стороннюю библиотеку, если вам нужны классные всплывающие окна с приятными визуальными эффектами.
Но если вы просто хотите использовать обычное окно сообщения от Microsoft для своего приложения WPF, вот реализация, удобная для MVVM/юнит-теста:
Первоначально я думал, что просто унаследую окно сообщения и оберну его интерфейсом, но не смог из-за того, что в окне сообщения нет общедоступного конструктора, поэтому вот «простое» решение:
Декомпилируя окно сообщения в Visual Studio, вы можете увидеть все перегрузки методов, я проверил, какие из них мне нужны, затем создал новый класс и добавил методы, обернул его интерфейсом и та-да!Теперь вы можете использовать ninject для привязки интерфейса и класса, внедрить его и использовать Moq для модульного тестирования и т. д.
Создайте интерфейс (добавлено лишь несколько перегрузок, поскольку они все мне не нужны):
public interface IMessageBox
{
/// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>
MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button);
/// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>
MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon);
/// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>
MessageBoxResult Show(string messageBoxText, string caption);
}
Затем у нас есть класс, который будет от него наследовать:
public class MessageBoxHelper : IMessageBox
{
/// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>
public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button,
MessageBoxImage icon)
{
return MessageBox.Show(messageBoxText, caption, button, icon, MessageBoxResult.None,
MessageBoxOptions.None);
}
/// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>
public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button)
{
return MessageBox.Show(messageBoxText, caption, button, MessageBoxImage.None, MessageBoxResult.None,
MessageBoxOptions.None);
}
/// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>
public MessageBoxResult Show(string messageBoxText, string caption)
{
return MessageBox.Show(messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None,
MessageBoxResult.None, MessageBoxOptions.None);
}
/// <summary>Displays a message box that has a message and that returns a result.</summary>
public MessageBoxResult Show(string messageBoxText)
{
return MessageBox.Show(messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None,
MessageBoxResult.None, MessageBoxOptions.None);
}
}
Теперь просто используйте это при внедрении и т. д., и у вас есть хлипкая абстракция, которая сделает свое дело...это нормально, в зависимости от того, где вы будете его использовать.В моем случае это простое приложение, предназначенное только для выполнения нескольких задач, поэтому нет смысла разрабатывать решение.Надеюсь, это кому-то поможет.