Subscribe weakly to events obtained by reflection
I'm making a MessageBox
control in WPF using the MVVM pattern. This MessageBox
will be used in multiple applications with different appearances, so ideally I want to keep code out of code-behind.
I'm trying to make the MessageBox
appear when an event is raised, specified in the declaration of the MessageBox
.
For example, this would be specified in the XAML of the window in which the MessageBox
should appear.
<dialog:MessageBox
ShowOnEvent="EventRaised"
EventContext="{Binding}"
Message="I am a message box"
IconType="Warning"
ButtonsType="YesNo" />
The way this currently works: in the MessageBox
ViewModel I'm using reflection to get the EventInfo
for the event, then subscribing directly:
if (eventContext != null && showOnEvent != string.Empty)
{
EventInfo eventInfo = eventContext.GetType ().GetEvent (showOnEvent);
if (eventInfo != null)
{
eventInfo.AddEventHandler (eventContext, eventHandler);
}
else
{
Debug.WriteLine (string.Format ("Dialog: Couldn't find event {0} on {1}, check event name.", showOnEvent, eventContext));
}
}
This shows the MessageBox
when the event is raised, as expected.
However, the event handler means that the MessageBox
ViewModel is not GC'd when the main window's View is disposed. This means that if another view for the main window is created, another MessageBox
is created, so if the event is raised, both MessageBox
s will show.
I tried getting around this by u开发者_开发百科sing a WeakEventManager
, but the Weak Event Patterns documentation specify that an implementation of WeakEventManager
should only handle one event - which means that I can't make a ShowOnEventEventManager
with the event name as a string property and subscribe using that.
Does anyone have any ideas for the best way to go about this?
Having a weak event won't solve your problem because you won't be unsubscribed until the GC decides to run (unless you're explicitly calling GC.Collect()
). As Will suggests in the comments, you can try to unsubscribe at the appropriate time or what might be even easier is just have your MessageBox check if it IsLoaded
before showing itself.
I wouldn't worry about code-behind in your MessageBox unless you know of some reason why it would hurt its reusability. It's fine to have MessageBox code reference its view directly as long as the consumers of the MessageBox have a MVVM-friendly API.
The PRISM EventAggregator
implements eventing using weak references by default. You need to be able to alter the code where events are published to implement this in your app.
There are proper code examples at the linked page as well as the obligatory flow diagrams. The event aggregator is fairly simple to use: you Publish
with a strongly typed payload and Subscribe
in as many places as you need. (And it's free to download)
精彩评论