EventAggregator с призмой в приложении MVVM Wpf, виртуальная машина подписчика не может получить данные

Я пытался реализовать призму EventAggregator в моем приложении MVVM Wpf. Я сделал это и собираюсь показать, вдохновленный этим сообщением в блоге: Prism EventAggregator

Общая цель - отправить список другой модели просмотра.

У издателя есть ViewModel, где я пытаюсь опубликовать ObserverableCollection.

Класс события:

public class RoomsSelectedEvent : PubSubEvent<ObservableCollection<Room>>
{

}

Я использую Unity для внедрения интерфейса IEventAggregator, например:

Метод запуска Unity:

protected override void OnStartup(StartupEventArgs e)
{
        base.OnStartup(e);

        //view & viewModels
        _container = new UnityContainer();     
        _container.RegisterType<IViewMainWindowViewModel, MainWindow>();
        _container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
        _container.RegisterType<IViewBookingViewModel, BookingView>();
        _container.RegisterType<IViewBookingViewModel, BookingViewModel>();
        _container.RegisterType<IViewContactViewModel, ContactDetailsView>();
        _container.RegisterType<IViewContactViewModel, ContactViewModel>();
        _container.RegisterType<IGetRoomsService, GetRoomsService>();
        _container.RegisterType<IPostReservationService, PostReservationService>();
        _container.RegisterType<IGetReservationsListService, GetReservationsListService>();

        //types
        _container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
        _container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));

        _container.Resolve<MainWindow>().Show();
}

В моей модели ViewModel издателя я создаю событие.

Модель просмотра издателя:

public class BookingViewModel : INotifyPropertyChanged, IViewBookingViewModel
{
    //aggregator 
    protected readonly IEventAggregator _eventAggregator;

    //commands
    public ICommand ContinueCommand { get; set; }

    public ObservableCollection<Room> RoomsList { get; private set; }        
    public ObservableCollection<RoomList> DropDownRooms { get; private set; }
    public ObservableCollection<CustomerList> DropDownCustomers { get; private set; }

    //enities
    private readonly IDialogService<ContactDetailsView> _dialogServiceContactView;
    private readonly IGetRoomsService _getRoomsService;

    public BookingViewModel(IDialogService<ContactDetailsView> dialogServiceContactview, IGetRoomsService GetRoomsService, IEventAggregator eventAggregator)
    {
        // Injection
        _dialogServiceContactView = dialogServiceContactview;
        _getRoomsService = GetRoomsService;
        _eventAggregator = eventAggregator;

        //Instantiation
        RoomsList = new ObservableCollection<Room>();

        //Instantiation commands
        ContinueCommand = new RelayCommand(ContinueCommand_DoWork, () => true);

    }

    // Continue Command
    public void ContinueCommand_DoWork(object obj)
    {
        ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();

        ObservableCollection<Room> RoomsListNew = new ObservableCollection<Room>();

        RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);

        //Publish event:
        _eventAggregator.GetEvent<RoomsSelectedEvent>().Publish(RoomsSelected);

        _eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe((data) => { RoomsListNew = data; });

        // Open new dialog
        _dialogServiceContactView.ShowDialog();
    }

}

В моей модели просмотра подписчика мне нужно получить этот список.

Модель просмотра подписчика:

public class ContactViewModel : IViewContactViewModel, INotifyPropertyChanged
{
    //aggregator 
    protected readonly IEventAggregator _eventAggregator;

    //properties
    public ObservableCollection<Room> SelectedRooms { get; set; }

    public ContactViewModel(IEventAggregator eventAggregator)
    {
        //Injection
        _eventAggregator = eventAggregator;

        //Subscripe to event
        _eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe(handleSelectedRoomsEvent);
        _eventAggregator.GetEvent<RoomsSelectedEvent>().Subscribe((data) => { SelectedRooms = data; });

    }

    private void handleSelectedRoomsEvent(ObservableCollection<Room> room)
    {
        if (room != null)
        {
            SelectedRooms = room;
        }
    }

    public ObservableCollection<Room> Rooms
    {
        get { return SelectedRooms; }
        set { SelectedRooms = value; NotifyPropertyChanged(); }
    }

}

Когда я отлаживаю, метод handleSelectedRoomsEvent даже не вызывается. Так же:

Subscribe((data) => { SelectedRooms = data; });

Здесь data и SelectedRooms все время равны нулю. В режиме отладки я вижу, что событие запущено.

Надеюсь, кто-то увидит, что здесь может быть не так. Примечание: использование версии prism.Core 6.1. (Также пробовали призму версии 5.0)

Добрый день!

Забыл упомянуть, что, на мой взгляд, я использую ListBox, где itemsSource должен получать selectedRooms (тот, на который я подписываюсь)

<ListBox Foreground="#ffffff" Background="#336699" BorderBrush="#336699" ItemsSource="{Binding Rooms, Mode=TwoWay}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding RoomNumber}"/>
                        <TextBlock Text=" - "/>
                        <TextBlock Text="{Binding Beds}"/>
                        <TextBlock Text=" - "/>
                        <TextBlock Text="{Binding RoomType}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

person Mikkel    schedule 16.04.2016    source источник
comment
Возвращает ли FilterSelectedRooms что-то не нулевое каждый раз, когда вы запускаете это?   -  person R. Richards    schedule 16.04.2016
comment
Вы уверены, что конструктор ContactViewModel был вызван до публикации события?   -  person hypercodeplace    schedule 16.04.2016
comment
@hcp, если конструктор не вызывается и подписка происходит при вызове конструктора, следовательно, мы не увидим активированный код подписки?   -  person StepUp    schedule 16.04.2016


Ответы (2)


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

Ознакомьтесь с этим простым примером того, как следует настроить приложение.

https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/HelloWorld

person Community    schedule 16.04.2016
comment
Так вы говорите, потому что в моем контейнере Unity я определил в классе App, что EventAggrgator не работает? Или это только для того, чтобы мне было легче :)? - person Mikkel; 17.04.2016
comment
С помощью предоставленного вами кода невозможно определить, в чем проблема, потому что вы не используете полную библиотеку Prism. В вашем коде / архитектуре есть что-то, что вызывает проблему. Например, если у вас есть событие, определенное в общем проекте, оно не сработает. - person ; 18.04.2016

Поскольку конструктор ContactViewModel не вызывается до публикации события, необходимо просто отправить параметр (ObservableCollection<Room> room) в конструктор ContactViewModel. Более того, вы должны создать экземпляр своего ContactViewModel из BookingViewModel, следовательно, вы должны режим создания экземпляра ContactViewModel с App.xaml.cs до ContactViewModel.

Позвольте мне показать полный пример.

App.xaml.cs:

protected override void OnStartup(StartupEventArgs e)
{
        base.OnStartup(e);
        _container = new UnityContainer();
        _container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
        _container.RegisterType<IViewMainWindowViewModel, MainWindow>();
        _container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
        _container.RegisterType<IViewBookingViewModel, BookingView>();
        _container.RegisterType<IViewBookingViewModel, BookingViewModel>(new ContainerControlledLifetimeManager());
        _container.RegisterType<IViewContactViewModel, ContactDetailsView>(new ContainerControlledLifetimeManager()); 
        _container.RegisterType<IGetRoomsService, GetRoomsService>();
        _container.RegisterType<IPostReservationService, PostReservationService>();
        _container.RegisterType<IGetReservationsListService, GetReservationsListService>();
        _container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));

        _container.Resolve<MainWindow>().Show();
    }

BookingViewModel:

public void ContinueCommand_DoWork(object obj)
{
   ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();
   ObservableCollection<Room> RoomsListNew = new ObservableCollection<Room>();
   RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);           
   _unityContainer.RegisterType<IViewContactViewModel, ContactViewModel>(new InjectionConstructor(RoomsSelected));            
   _dialogServiceContactView.ShowDialog(_unityContainer);
 }

Конструктор ContactViewModel:

public ContactViewModel(ObservableCollection<Room> room)
{            
   // Initialize dropdown data for titlelist
   DropDownTitle = GenerateDropDownDataForTitle();
   DropDownCountry = GenerateDropDownDataForCountry();
   ContactModel = new ContactDetails(1, "", "", "", "", 1);
   // Initialize commands
   BookCommand = new RelayCommand(BookCommand_DoWork, () => true);
   BackCommand = new RelayCommand(BackCommand_DoWork, () => true);
   if (room != null)
   {
       SelectedRooms = room;
   }
}
person StepUp    schedule 16.04.2016
comment
Это просто не выглядит красиво, если использовать контейнер Unity внутри команды Continue: P - person Mikkel; 17.04.2016