0
votes

I have been trying to implement the prism EventAggregator in my MVVM Wpf application. I made this I'm about to show with inspiration from this blog post: Prism EventAggregator

The overall goal is sending a list to the other viewModel.

Have a ViewModel as a Publisher, where I'm trying to publish an ObserverableCollection.

Event class:

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

}

I'm using Unity to inject the IEventAggregator interface, like:

Unity startup method:

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();
}

In my publisher ViewModel, I'm creating the event.

Publisher 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();
    }

}

On my Subscriber ViewModel, I need to retrive this list.

Subscriber ViewModel:

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(); }
    }

}

When I debug, the handleSelectedRoomsEvent method is not even being called. Also the:

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

Here data and SelectedRooms is equal to null all the time. I can see in debug mode that an event is fired.

Hope someone can see what could be wrong here. Note: Using the prism.Core 6.1 version. (Have also tried prism version 5.0)

Good day!

Forgot to mention that in my view, I'm using a ListBox where the itemsSource should get the selectedRooms (the one I'm subscribing to)

<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>
2
Is the FilterSelectedRooms returning something not null every time you run this?R. Richards
Are you sure that constructor of ContactViewModel had been invoked before event has been published?hypercodeplace
@hcp if constructor is not invoked and subscription happens when constructor is invoked, consequently we will not see invoked code of subscription?StepUp

2 Answers

2
votes

You're not doing it right. You need to be creating a bootstrapper in the OnStartup and nothing else. You should not be creating an instance of the Unity container. This is all done for you automatically.

Check out this simple sample on how you should setup your app.

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

0
votes

As a constructor of ContactViewModel is not invoked before event is published, it is necessary just send a parameter(ObservableCollection<Room> room) into constructor of ContactViewModel. Moreover, you should instantiate your ContactViewModel from BookingViewModel, consequently you should mode instantiating of ContactViewModel from App.xaml.cs to ContactViewModel.

Let me show the full example.

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);
 }

Constructor of 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;
   }
}