Как открыть новое окно с помощью MVVM Light Toolkit

Я использую набор инструментов MVVM Light в своем приложении WPF. Я хотел бы знать, как лучше всего открыть новое окно из существующего. У меня есть MainViewModel, который отвечает за MainWindow моего приложения. Теперь в MainView при нажатии кнопки я хотел бы открыть второе окно поверх него. Я RelayCommmand привязан к Command Button. В методе RelayCommand я могу создать новый объект окна и просто вызвать Show(), примерно так:

var view2 = new view2()
view2.Show()

но я не думаю, что ViewModel должен отвечать за создание нового объекта view2. Я прочитал этот пост WPF MVVM Get Parent from View MODEL, где Bugnion предложил передать сообщение view1 от viewmodel1, а затем view1 должен создать новый view2. Но я не уверен, что он на самом деле имел в виду, передавая сообщение view1? Как view1 обработать сообщение? В этом коде что ли?

С уважением, Набил


person nabeelfarid    schedule 02.08.2010    source источник
comment
см. stackoverflow.com/questions/16993433/   -  person reggaeguitar    schedule 25.04.2014


Ответы (6)


Передача сообщения из ViewModel1 в View1 означает использование возможности обмена сообщениями в MVVM Light Toolkit.

Например, ваша ViewModel1 может иметь команду под названием ShowView2Command, тогда она отправит сообщение для отображения представления.

public class ViewModel1 : ViewModelBase
{
    public RelayCommand ShowView2Command { private set; get; }

    public ViewModel1() : base()
    {
        ShowView2Command = new RelayCommand(ShowView2CommandExecute);
    }

    public void ShowView2CommandExecute()
    {
        Messenger.Default.Send(new NotificationMessage("ShowView2"));
    }
}

View1 зарегистрируется для получения сообщений в своем коде и отобразит View2, когда получит правильное сообщение.

public partial class View1 : UserControl
{
    public View1()
    {
        InitializeComponent();
        Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
    }

    private void NotificationMessageReceived(NotificationMessage msg)
    {
        if (msg.Notification == "ShowView2")
        {
            var view2 = new view2();
            view2.Show();
        }
    }
}
person Matt Casto    schedule 02.08.2010
comment
Спасибо за ответ, Мэтт. Есть ли какие-либо другие подходы / лучшие практики для открытия новых представлений в mvvm, кроме обмена сообщениями? - person nabeelfarid; 03.08.2010
comment
Еще один подход, который используют люди, - это иметь служебный стиль класса, который используется для открытия представления. Ваша ViewModel будет работать с интерфейсом этой службы для отображения ChildWindow, MessageBox или чего-то еще. Это особый фаворит тех, кто хочет нулевого кода в коде программной части своего представления. Кроме того, это немного более проверяемо, поскольку вы можете имитировать службу и утверждать, что был вызван метод для отображения вашего представления. - person Matt Casto; 03.08.2010
comment
да, я тоже видел, как люди говорили об этом. Но то, что я не понимаю в этом подходе, заключается в том, что когда вы открываете дочернее окно из модели представления, используя некоторую службу, можно сказать IDialogService.OpenChild (), как бы вы установили владельца дочернего окна в качестве модели просмотра, вызывающей IDialogService. OpenChild () не знает или имеет ссылку на собственное представление? - person nabeelfarid; 04.08.2010
comment
Вам не нужно устанавливать владельца для ChildWindow, по крайней мере, мне никогда не приходилось этого делать. Если у вас должен быть владелец, вы, вероятно, можете получить корневой визуальный элемент приложения и использовать его. - person Matt Casto; 04.08.2010
comment
Есть несколько причин, по которым нужно установить владение дочерним окном. Отношения владения требуют определенного поведения, в том числе в отношении минимизации, максимизации и восстановления и т. Д. (msdn.microsoft.com/en-us/library/). Что касается использования RootVisual, это снова означает отправку сообщения в представление, потому что rootvisual будет доступен в коде, а не в модели просмотра? - person nabeelfarid; 10.08.2010
comment
Вы можете вызвать App.Current.RootVisual из любого места. Однако это добавляет в ваш класс ViewModel зависимость, которая должна быть инкапсулирована для модульного тестирования. - person Matt Casto; 10.08.2010
comment
Это довольно круто, но в то же время напоминает мне MFC 1997 года с использованием Message Pumps ... - person Mike Caron; 15.07.2013
comment
Такое решение кажется хорошим разделением проблем. Я использовал его в своем сценарии, где я хочу показать SomeView с помощью RelayCommand из ViewModel. Мне нужна подсказка, поскольку я использовал это решение, а событие NotificationMessageReceived не запускается. Во-первых, я добавил к ViewModel RelayCommand, который отправляет сообщение уведомления, это сообщение регистрируется под представлением в конструкторе. В любом случае это странно, поскольку SomeView нигде не создается. Не могли бы вы мне посоветовать? - person komizo; 29.08.2014
comment
Я использовал другое решение из вопроса 5829413. Это решение выглядит хорошо. Я использовал его - работает, но я не уверен, хорошее ли это решение, так как мне пришлось добавить еще один нестатический конструктор в App.cs. Не могли бы вы подсказать, где мне правильно зарегистрировать это сообщение, которое отвечает за отображение View? Приложение - хорошее решение? А может локатор, но локатор? Буду признателен за совет. - person komizo; 29.08.2014
comment
View2 - это страница или пользовательский элемент управления? - person user3260977; 16.11.2016
comment
Как заявил @MattCasto, это можно сделать через службу обмена сообщениями или просмотр. Лоран Багнион (автор MVVM Light) написал статью MSDN о службах обмена сообщениями и просмотра в MVVM здесь: msdn.microsoft.com/en-us/magazine/jj694937.aspx - person Derek W; 27.04.2017
comment
Если кому-то интересно - и Messenger, и NotificationMessage можно найти в пространстве имен GalaSoft.MvvmLight.Messaging. - person itsho; 29.08.2017
comment
@MattCasto: Работает неплохо, спасибо! Как я могу убедиться, что установлен правильный DataContext, когда я вызываю ChildView (DataContext = ChildViewModel) из MainView (DataContext = MainViewModel) - person br0ken.pipe; 23.02.2019
comment
Концепция также сводится к тому, что делает Prism, я думаю, но должен ли View открывать другой View? Я считаю, что это слишком много логики в представлении. MVVM становится невыносимым. - person ed22; 16.03.2019

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

Например, я создаю проект для клиента, который хочет иметь 2 пользовательских интерфейса - один будет фундаментально отличаться во всех смыслах с точки зрения представления. Горизонтальные вкладки против вертикальной RadPanelBar (думаю, Accordion) для навигации. Оба этих представления могут указывать на одну и ту же модель представления - когда пользователь щелкает вкладку «Порядок работы» в представлении 1, он запускает ту же команду «WorkOrderCommand», которая запускается в заголовке рабочего задания на панели панели.

В модели кода программной части вам придется закодировать два отдельных события. Здесь вам нужно только закодировать один.

Кроме того, это позволяет дизайнеру, использующему Blend, создавать любой макет, который они хотят. Пока у них есть перехватчики (элемент управления EventToCommand), мне (как разработчику) все равно, как выглядит конечный продукт.

Слабое сцепление невероятно мощно.

person Scott Silvi    schedule 21.01.2011

Вы можете сделать это так же, как вам нужно создать некоторые события и зарегистрировать их в view, вызвать их в модели просмотра. И открыть это всплывающее окно.

Как этот пример

public class Mainclass : MainView
{
    public delegate abc RegisterPopUp(abc A);
    public RegisterPopUp POpUpEvent ;

    public RelayCommand ShowCommand { private set; get; }  


    public void ShowCommand() 
    { 
        ShowCommand("Your parameter");
    } 
}

внутри вида MainView mn=new MainView();

Зарегистрируйте событие здесь, например, thake mn.POpUpEvent +=, чем дважды нажмите кнопку табуляции

а в методе всплывающего окна регистров справа код для открытия всплывающего окна.

person Hoshiyar    schedule 23.11.2010

Если я не упускаю суть здесь - если бы я должен был использовать код позади, то почему бы напрямую не реализовать событие button_click и не открыть второе представление?

Кажется, что Bugnion предлагает view1 -> нажатие кнопки -> команда реле -> viewmodel1 -> message -> view1 -> view1.cs -> open view 2.

В любом случае вы собираетесь пожертвовать тестируемостью, написав код программной части, так зачем же идти таким длинным путем?

person Pratz    schedule 24.08.2010
comment
длинный маршрут гарантирует, что: когда вы тестируете свою модель просмотра, вы можете по крайней мере проверить, что сообщение / запрос было передано в широковещательном режиме, чтобы открыть новое представление. Вы можете обернуть код запроса сообщения в IDialogService, чтобы сделать его фиктивным во время тестирования. - person nabeelfarid; 25.08.2010
comment
Я согласен с Пратцем, это немного безумие - идти таким длинным маршрутом. - person Vincent; 28.10.2010
comment
Код позади метода подходит для небольшого приложения с небольшим количеством окон / представлений. Дополнительная сложность кажется излишней, если у вас есть главное окно, которое время от времени открывает второе окно, просто чтобы отобразить некоторые детали. Если приложение станет большим, код не будет хорошо масштабироваться, и тестирование пострадает. - person srock; 05.09.2013

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

В вашем случае вы можете создать интерфейс IWindowManager или что-то подобное, у которого есть требуемый метод. Это может быть реализовано в вашем слое просмотра. Недавно я написал небольшой пост в блоге, демонстрирующий, как абстрагировать поведение диалога из модели просмотра. Подобное приложение можно использовать для любой службы, связанной с пользовательским интерфейсом, такой как Navigation, MessageBoxes и т. Д.

Эта ссылка может быть полезной для вас http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html

Многие люди также используют подход запуска событий из моделей представления, которые подписаны на файл view.cs, и оттуда выполняется MessageBox или любое другое действие, связанное с пользовательским интерфейсом. Мне лично нравится подход внедрения сервисов, потому что тогда вы можете предоставить несколько реализаций одного и того же сервиса. Простым примером может служить обработка навигации в приложениях Silverlight и Windows Phone 7. Вы можете использовать одну и ту же модель представления, но внедрить разные реализации службы навигации в зависимости от типа приложения.

person Nilesh Gule    schedule 18.05.2011

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

  1. Создать DialogCloser класс
    public static class DialogCloser
    {
        public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged));

        private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null) window.Close();
        }

        public static void SetDialogResult(Window target, bool? value)
        {
            target.SetValue(DialogResultProperty, value);
        }
    }
  1. Создайте базовую модель представления, унаследованную от GalaSoft.MvvmLight.ViewModelBase, с дополнительными элементами. После этого используйте эту модель просмотра как основу для других моделей просмотра.
    bool? _closeWindowFlag;
    public bool? CloseWindowFlag
    {
        get { return _closeWindowFlag; }
        set
        {
            _closeWindowFlag = value;
            RaisePropertyChanged("CloseWindowFlag");
        }
    }

    public virtual void CloseWindow(bool? result = true)
    {
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
        new Action(() =>
        {
            CloseWindowFlag = CloseWindowFlag == null ? true : !CloseWindowFlag;
        }));
    }
  1. В представлении свяжите свойство зависимостей DialogCloser.DialogResult со свойством CloseWindowFlag в базовой модели представления.

Затем вы можете открыть / закрыть / скрыть окно из модели просмотра.

person Touhid Alam    schedule 23.10.2014
comment
Не могли бы вы привести пример открытия окна? Насколько я понимаю, это закрывает только окно / представление. - person br0ken.pipe; 23.02.2019