Event Aggregator: Can Event raise another event?

Topics: Prism v4 - Silverlight 4
Feb 17, 2011 at 6:29 AM

Hello,

I have the following scenario, The ToolbarViewmodel resides within the Navigation Module. When the New Button on the toolbar is clicked this event shall be raised to notify the NavigationViewModel.

_eventAggregator.GetEvent<NotifyNavigationViewModelEvent>().Publish(null);

In the Ctor of NavigationViewModel I have subscribed to the event like this:

_compositeEventAggregator.GetEvent<NotifyNavigationViewModelEvent>().Subscribe(OnAddNewContact);


All it does is to create a new instance of type Contact. Hold a reference to it in a collection for later saving and passes it to another event to be sent to the 
Contact Module. (Question 1: I read in the Prism developer guide: It is not recommended to modify the payload object from within a callback delegate because several threads could be 
accessing the payload object simultaneously. You could have the payload be immutable to avoid concurrency errors. However how am I supposed to send my objects around then. What is the recommended way if not by Events?)

public void OnAddNewContact(object o)
        {
            var contact = new Contact();
            Contacts.Add(contact);
 
            _compositeEventAggregator.GetEvent<NotifyContactModuleEvent>().Publish(contact);
        }


Within the Contact Module's ctor I subscribe to the second event like this:
 
_eventAggregator.GetEvent<NotifyContactModuleEvent>().Subscribe(DisplayContact);

All it does is to inject the Contact object into the View so that it can be filled in.

public void DisplayContact(Contact contact)
        {
            var contactViewModel = _container.Resolve<ContactViewModel>();
            var view = new ContactView(contactViewModel);
            view.Initialize(contact);
            _regionManager.RegisterViewWithRegion(Constants.MainRegion, () => view);
        }
 

The first event from Toolbar to NavigationViewModel works perfectly fine, but then at the time the second event NotifyContactModuleEvent is meant to be raised, it doesn't do anything. 
Question 2: Why is that? Am I missing a concept here?

The events are implemented like this:

public class NotifyContactModuleEvent : CompositePresentationEvent<Contact>
    {
 
    }

public class NotifyNavigationViewModelEvent : CompositePresentationEvent<object>
    {
 
    }

ModulesCatalog:

<Modularity:ModuleInfo Ref="X.Modules.ContactModule.xap" ModuleName="ContactModule" ModuleType="X.Modules.ContactModule.ContactModule, X.Modules.ContactModule, Version=1.0.0.0, Culture=neutral, PublicToken=null"/>
        <Modularity:ModuleInfo Ref="X.Modules.NavigationModule.xap" ModuleName="NavigationModule" ModuleType="X.Modules.NavigationModule.NavigationModule, X.Modules.NavigationModule, Version=1.0.0.0, Culture=neutral, PublicToken=null">
            <Modularity:ModuleInfo.DependsOn>
                <sys:String>ContactModule</sys:String>
            </Modularity:ModuleInfo.DependsOn>
        </Modularity:ModuleInfo>     
 
Many Thanks for your advice on this,
Houman 
Feb 17, 2011 at 6:18 PM

Hi Houman,

From my understanding of the note in the Prism documentation, the suggestion is to avoid modifying the payload object from within the delegate callback; there isn’t any recommendation regarding not to publish an event from within the callback of another event, so there is no reason for you not to do so.

The unexpected behavior you’re experiencing might be caused by the fact that the region in which you’re trying to add the ContactView could be a ContentControl (that is, a SingleActiveRegion).

Additionally, you should take into account that the View Injection Approach could be more suitable for your scenario, as it is useful for cases in which you need explicit programmatic control on when the views are added to a region. For example, on the DisplayContact delegate you could do something like this:

var contactViewModel = _container.Resolve<ContactViewModel>();
var view = new ContactView(contactViewModel);
view.Initialize(contact);
_regionManager.Regions[Constants.MainRegion].Add(view);

You might find the UI Composition Chapter of the Prism MSDN documentation useful to this purpose.

If you keep experiencing this kind of problems, it would be helpful if you could provide us with a repro sample, so that we can help you determine which is the cause for it.

I hope you find this information useful.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

 

Mar 23, 2011 at 7:30 AM

Hi Miguel,

 

I don't get the event aggregators work consistently and it scares me off going into production like this. I have applied your suggestions regarding View Injection Approach and still it doesnt work.

And it seems impossible to me to debug through it.  Is there anyway you could provide me with your email and I would send you a copy of the app?
I am quite stuck here.

 

Many Thanks,

Houman 

Mar 23, 2011 at 9:46 AM
Edited Mar 23, 2011 at 9:48 AM

Hey guys,

I have found out the problem after pulling half of my hair out.

The second event subscription needs to be kept alive (strong reference) otherwise its possible that its sometimes garbage collected and hence it remains unresponsive.

_eventAggregator.GetEvent().Subscribe(DisplayContact, ThreadOption.UIThread, true);

This way it would work fine.  Since I seem to have now a strong event subscription between a ViewModel and a different Module, what does that mean for the memory management?

ToolbarViewModel --publishes--> EventA
NavigationViewModel --Subscribes (Weak)--> Event A
                                 => Publishes EventB
ContactModule --Subscribes (Strong)--> EventB


1) Does this mean the NavigationViewModel (part of NavigationModule) can't be garbage collected as long as ContactModule is strongly-subscribed to the former? Or is it the other way around? I am now quite confused. :)

2) What determines the life cycle of a weak event? How comes my second event is garbage collected so fast if the subscription isn't set to be strong?
It seems ContactModule looses its subscription to NavigationViewModel if the event was set to be weak. Could it generally be that Prism Module classes have a shorter life span?


Many Thanks for your advice on this matter,
Houman

Developer
Mar 23, 2011 at 3:29 PM

Hi Houman,

When you set the keepSubscriberReference alive parameter to true on the Subscribe method, you specify that a strong reference should be maintained between your event and its subscriber. Therefore, you will grant that the subscriber won't be garbage collected until you manually unsubscribe to that event.

In your case, since your subscriber is the ContactModule, that's the one that won't be garbage collected until you've unsubscribed from the event.

On the other hand, a weak event subscription won't hold a strong reference to the Subscriber, hence it will be active as long as the subscriber is kept alive. For example, if you subscribe to an event from within your ContactModule, if you don't have any other references to it, that instance will eventually be garbage collected. Note that it's usual to have the IModule be garbage collected; instead, you should create an instance of a controller or a similar class, and make that instance keep alive, instead of the IModule itself.

I hope you find this helpful.

Guido Leandro Maliandi 
http://blogs.southworks.net/gmaliandi

Mar 23, 2011 at 3:52 PM

Hi Guido,

Many Thanks for your response.

It makes now more sense to me. And by the way it looks like I have a non-optimal architecture.  If I would like to apply your suggestion here and don't let a module subscribing to my event,
and would have to utilize a controller class instead; how would I design such controller class?
- Do you have a simple example for it how to make it fit there? e.g. should it be part of the Module project or rather a class within the Infrastructure project?
- Do I have to make it inherit from anything special or just a plain class?

Its just that I would like to have the architecture right from beginning, as much as possible. :)

Thank you,
Houman

Developer
Mar 23, 2011 at 4:29 PM
Edited Mar 23, 2011 at 4:30 PM

Houman,

Based on my understanding, a possible way to achieve the aforementioned idea would be to use a controller class inside the module project; the class shouldn't inherit from anything special unless you specifically require it.

I hope you find this helpful.

Guido Leandro Maliandi 
http://blogs.southworks.net/gmaliandi