Prism Event Aggregation - subscriber not triggered
Asked Answered
H

2

19

I'm working on implementing an event aggregation with Prism. I have a few modules, and I want each of them to subscribe to events that tells them when they are requested. I started out doing an all plain example with both subscribed and publisher in the shell. No problems there. Now; when I move the subscribers out to my modules they don't get triggered. What's even more odd is that it actually has worked a few times - all of which I've been pending in a breakpoint. So it seems to me to be some race condition, but I don't understand why.

Assumption made: I don't need to set up the IEventAggregator anywhere - e.g. registering in the IoC container? This is built into Prism such that I only have one instance of the event aggregator, right?

So, the question is basically how/where/when I should set up my subscribers. Is there a specific order on stuff etc? In my simplified example I have one module MyModule. The Bootstrapper will add MyModule to the catalog - making it initialized:

catalog.AddModule(typeof(MyModule));

MyModule will store the aggregator and use this for subscribing to the MyModuleRequestedEvent. It also uses a menu registry to register in the application menu. The idea is that eventually clicking in the menu should trigger the event - notifying MyModule that it has been requested. Then I want it to be MyModule's responsibility to figure out what to do further.

public MyModule(IEventAggregator aggregator, IApplicationMenuRegistry menu)
{
    _applicationMenu = menu;
    _aggregator = aggregator;
}

public void Initialize()
{
    var evnt = _aggregator.GetEvent<MyModuleRequestedEvent>();
    evnt.Subscribe(MyModuleRequested);
    _applicationMenu.RegisterMenuItem("MyModule", evnt);
}

public void MyModuleRequested(bool b)
{
    MessageBox.Show("MyModule requested");
}

Now, I have a button in my shell which will publish this event. The shell gets the same (?) event aggregator when resolved.

public Shell(IEventAggregator aggregator)
{
    InitializeComponent();
    var evnt = aggregator.GetEvent<MyModuleRequestedEvent>();
    EventTriggerButton.Click += (s, e) => evnt.Publish(true);
}

Notes:

  • Have verified that the event is published. Adding a subscriber in the shell too will make this subscriber receive the event.
  • Again; the subscriber in MyModule isn't triggered. However, it has - strangely - been on a few occasions.
  • I don't use the input to the event. It seemed like you needed to have some input-type, so I just went with a dummy bool. Can I get rid of this..?
Hectoliter answered 27/11, 2009 at 21:47 Comment(2)
It does sound like you're getting two EventAggregators; use the debugger to create an Object IDs for them both so you can check and let us know.Mulry
Thanks for the tip! Did it, and it is in fact the same EventAggregator that is used for subscribing in the module and for publishing. Got into the Shell constructor first. Did the "Make Object ID". Got 1#. Then hit breakpoint in MyModule.Initialize, and verified that the aggregator there had ID 1#. Also when hitting the button to trigger publish I see that the aggregator used is 1#.Hectoliter
F
33

The Prism Event Aggregator uses Weak References to link to the events. This is to prevent memory leaks from event handlers.

Once a module initializer has been run it's disposed of, so your event handler is being destroyed before the event is being fired. You can tell Prism to keep the event handler around by using an overload of Subscribe.

evnt.Subscribe(MyModuleRequested, true);

As a pattern, I tend to put any event subscribers in a separate class, and call that class from the modules Initialize method. That way the events stay alive but separate while the module is still destroyed.

Florrie answered 28/11, 2009 at 0:16 Comment(2)
Perfect! And your pattern makes sense - so I will put a minimal event handler in a separate class. Thanks..!Hectoliter
I also had this problem, adding a view to a Prism region stops my module from being disposed, am I doing something wrong with my region registration? Do you know of any documentation about recommended Module lifetime and registering views?Oxidimetry
H
2

So, I just got a theory, but no time to test it right now.. Will do tomorrow.

Question: Will adding modules to the ModuleCatalogue keep them alive? I assumed it would. Hence - the MyModule should stay alive - and then will be triggered when the event is published.

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();
    catalog.AddModule(typeof(MyModule));
    return catalog;
}

However, if this doesn't keep the module alive it is obvious that it will have a hard time respond to the event. The module-object dies, but it doesn't unsubscribe - hence I'll see the subscriber in the EventAggregator list, but the subscriber isn't around anymore. Also; I mentioned that I it does in fact work occasionally - which would be the case if the garbage collector didn't have time to take out the trash before the event is triggered.

Does this sound like the case? If so - I haven't thought of a solution yet so you're welcome to suggest one in a different reply-thread..

So; what is the ModuleCataloge anyway? Just a list kept for Initialization and then thrown away?

Hectoliter answered 27/11, 2009 at 23:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.