Routing an event through multiple classes in C# - c#

A common scenerio we are running into with our current application is where we need to route and event through several classes.
Here is a sample class heirarchy.
ActionManager
MainWindow
PresentationManager
MenuManager
Menu
MenuButton
The Menu subscribes to the click event of a MenuButton. It then creates a CustomAction object and raises an event that is subscribed to MenuManager. In the MenuManager event handler it in turn raises an event that is subscribed to by the PresentationManager, and so on.
Here is a sample of what is implemented for the PresentationManager:
void MenuManager_ActionGenerated(object sender, CustomActionEventArgs e)
{
if (ActionGenerated != null)
ActionGenerated(sender, e);
}
I was hoping that there would be a way that I could raise the event at the Menu level and receive it at the ActionManager level.
Is it bad practise what I am currently doing?

You can also look into Event Aggregator. A good example can be found at codeproject: Event Aggregator with Specialized Listeners

If what you've listed as your class hierarchy is actually your visual tree, it sounds like what you are describing is Routed events.
http://msdn.microsoft.com/en-us/library/ms742806.aspx
Personally, I get scared by having a lot of events. If you are not careful with unsubscription, they can extend the lifetime of your objects. Also, they may cause tight-coupling, reducing testability. In some cases using a Commanding pattern is a better approach.

I would try this CSharpMessenger Extended.

You can write your own SubscriptionManager.
By simplifying can be a Dicationary<string, List<Action<...>>>.
The key is the event-name, value is the List of Actions to run wen that even was raised.
So all yuor components subscribe to some specified event by adding its Action<..> to the list of specified event.
And when the even raised (always via SubscriptionManager) all Action<..>s from the list will be executed.
Just a basic idea. To make this production ready you need to code a bit more.
Good luck.

Related

Winforms subscribing to own events

One common thing I see developers doing in WinForms is forms/controls subscribing to their own events so you get
this.Load += new System.EventHandler(this.WelcomeQuickViewWF_Load);
this.Activated += new System.EventHandler(this.WelcomeQuickViewWF_Activated);
rather than
protected override void OnActivated(EventArgs e)
{
}
Now I know the second way is more Object Oriented, the first way is event driven and I tend to refactor towards overriding - is there any reason NOT to do this? What I don't want to be doing is making changes that are not really needed and purely an aesthetic choice.
By subscribing to your own event, you give up control over when your code is invoked when there are other subscribers to the event. By overriding the event raising method, you have full control over when your code should be invoked. You could invoke it before notifying subscribers or after. There's also no chance of your code being canceled on a cancelable event.
If the method is overridable, do so. Otherwise register as you have no other choice.
MSDN says that overriding the On* methods is the preferred technique for handling an event in a derived class:
The OnActivated method also allows derived classes to handle the event without attaching a delegate. Overriding this method is the preferred technique for handling the event in a derived class.
So I'd say the event handler approach is non-idiomatic.

Is there anyway to stop the flow of events in .NET?

that is a question I have been asking myself for a while.
Giving a certain flow of events, can I when handling one of them, stop the next ones to be raised?
For example, when collapsing a node which child was selected in a treeview (winform), the events are raised like that:
BeforeCollapse
BeforeSelect
AfterSelect
AfterCollapse
I could stop them by using a class member, but I was wondering whether there was a built-in function or just another way (a more elegant way) to achieve this, by acting directly on the events queue.
Any idea?
Not easily, no. The order of the events firing is controlled by the TreeView control class, and there is no built-in way to prevent events from firing. But you have a couple of options:
Create your own TreeView class that inherits from the base class,
then add a bool property to prevent the events from processing.
Then you can override BeforeCollapse, etc. to check the bool
before calling base.BeforeCollapse.
Just create a bool flag, and check the flag in each of the events.
No there is no way to do that for that type of event (you are asking for TreeView).
Like for example could be managed KeyEventArgs.Handled via built-in mechanism.
You can use some instance (boolean ?) value to manage the flow,
or you can, unsubscribe from the event that you don't want more recieve, but after subscribe to it again. Sounds rough solution, but sometimes turns out reasonable one.
even if the event are raised nothing will happen if you don't bind an event handler to them. In this case you can just remove the handler using the code below:
object.Event -= new EventHandlerType(your_Method)
Otherwise you should create your own custom control
according to OnBeforeCollapse you get an TreeViewCancelEventArgs which has an Cancel property. Setting this to true should stop the flow, but will also not collapse it.
Same goes for OnBeforeSelect.
The only times you can easily "cancel" an event is if the event handler has the CancelEventHandler delegate type. Even then it doesn't really cancel it as much as set a flag for the remaining events that makes it skip performing all the events subscribed to it.
If you did have a CancelEventHandler type (which these don't) you'd simply set Cancel to true on the event object itself in the handler.
Plenty of other answers give you suggestions for what you should o. I'd just go with your idea: set a 'event cancelled' flag in your control class, and check it. When the last event in the series gets called, reset it.

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 MessageBoxs will show.
I tried getting around this by using 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)

page/control lifecycle

I have usercontrol x in a page, within usercontol x is usercontrol z. I need to do something to usercontrol z after an event fires in usercontrol x. I understand this is very generic, but it just lays out a simple premise for my issue.
Is there a simple way to get a list of all events that fire in the page lifecycle whether I am subscribed to them or not to find out what events fire between Event A on the page and Event B on control z? I would think that there would be something like this when page tracing is turned on, but I don't see any events listed. I imagine that it would be something that the pipeline could generate, maybe I could use reflection to get a list?
I found this topic hard to search against in general, maybe I am using bad keywords...
I did find an old project on codeproject.com, but I was hoping that there would be something more elegant available by now.
*To clarify, I'm looking to generate a list after a postback of every potential event (subscribed or not), in firing order from init to unload, of all controls on a page.
THIS POST: Tracing all events in VB.NET
gets me a little bit closer, however these are lists on a per control basis, not in chronological order.
Enable trace with <%# Page Trace="true" %> in page attribute. that should display life cycle events in sequence.
Well if you're just looking to see what the events are in general, and in what order they fire, check out this article. As far as getting them...well, that depends on what you mean. It could be as simple as getting them through reflection:
EventInfo[] events = this.GetType().GetEvents();
The ASP.NET Page Life Cycle Overview on MSDN lists all of the events fired by a Page from PreInit to Unload. Most of these events fire on any class that inherits from System.Web.UI.Control as well.
Update
Now that I understand your intention better, one possible solution would be to loop through the events of every control and assign an event handler that logs the calling of the event. There is at least one way to retrieve all of the event handlers assigned to an event, and it shouldn't be too difficult to adapt that code to assign an event handler instead.

C#.NET - Can I change which class the designer adds events to?

I have my own custom control derived from System.Windows.Forms.TreeView which is present on the designer toolbox.
I add an instance of this custom control to my form (using the designer).
The purpose I created an inherited control is that I want to let the control itself handle its events since it's supposed to act as a View in a standard MVC design. I.e. when the node selection in the treeview changes I want the view to handle all interactions with the Model.
The problem is that when I add an event for my custom control (using the designer), the event gets added to my MainForm class. The only option in such case is to forward every event into my custom control, which doesn't feel very optimal.
Is there a way to control which class the event gets added to? I know it's technically possible since I can edit the auto-generated code for my MainForm and make the event being triggered into the custom control. That's clearly not the proper solution though.
Thanks.
why don't you just override some of the methods in your derived class, e.g.:
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
}
Tom Frey is right. If you want the behavior on a certain event to be handled by the class that would normally fire the event, the preferred technique is to override the appropriate method that fires the event to perform your task. The eventing infrastructure is overkill and not really designed for what you're trying to do here.
The reason is that you want every instance of your view to perform these actions. You don't want these actions to be dynamically assigned and determined at runtime by things external to your view (which is what events are for).
One important thing to include when you perform such an override, however, is the call to the base's implementation. If you don't, you may encounter strange errors:
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
// Do stuff you want done before here
base.OnNodeMouseClick(e);
// Do stuff you want done after here
}
Calling the base class will fire the event and may perform other actions besides. If you want something done before the event fires and any external subscribers do their thing, put it before the call to the base. If you want something done after all the events have fired and been handled, put it after. Contrived example: if your control handled some data binding, then you might want to verify the databinding exists before the call to base and you might want to verify it's valid after (in case a handler munged something).
From MSDN:
The OnNodeMouseClick method also
allows derived classes to handle the
event without attaching a delegate.
This is the preferred technique for handling the event in a derived
class.
Notes to Inheritors:
When overriding OnNodeMouseClick in
a derived class, be sure to call the
base class's OnNodeMouseClick method
so that registered delegates receive
the event.

Categories