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.
Related
I'm working on a semi-professional project and needed a custom view for one of my models. As far as I could figure out that meant creating a UserControl-inherited class, like so:
public partial class PopulationView : UserControl
{
...
}
Now this works wonders, and I've been able to do pretty much all I wanted, including custom painting with onPaint but I've noticed none of the events seem to be registering. I've had to attach multiple event listeners and in all instances I end up going back to my form and adding an event listener to my custom component's instance in the form. For example, I'd like to add a Click event listener. What I try first is simply
public partial class PopulationView : UserControl
{
private void PopulationView_Click(object sender, EventArgs e)
{
Debugger.Break();
}
}
of course attaching the function to the Click event through PopulationView.cs [Design]. That doesn't work so I end up adding a Click event listener on the instance of the component and adding the PopulationView_Click function to my form (MainForm.cs [Design]) instead and launching the appropriate function on the control.
I feel that I'm missing something very simple which is preventing me from being able to register events in my control directly and I'd appreciate any help with this. I dug around the interwebs but couldn't find anything relevant.
Update
Some of the comment make me think I should provide more detailed information about what I'm doing. The setup is sort of complicated but here's a summary:
I have a custom control inheriting from UserControl, called PopulationView
I have an instance of that control added to a form
Attaching event listeners through the design view of the form to the instance of the control (listener in form itself) works. Adding them through PopulationView's design view (listener in PopulationView) does not.
What complicates thing is I have a BackgroundWorker in my form which is in charge of generating the PopulationModel for the PopulationView. Once that BackgroundWorker is done, it uses a BeginInvoke to tell the PopulationView it's time to draw.
However, all events behave the way described even before the BackgroundWorker is triggered with RunWorkerAsync. Is it possible it's still interfering?
Editing the PopulationView constructor, I had accidentally deleted the call to InitializeComponent(), where all the Designer code was. Silly, really. If you're ever using the Designer, make sure you call InitializeComponent from your constructor.
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.
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.
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.
I am creating a class that derives from the WPF RichTextBox control and I need to execute some code in the copy and paste events.
I understand that, whenever possible, it is best practice to implement event-based code in a derived class by overriding the base class method that raises the event. However, no such method exists in this case, so is it acceptable for my derived class to add an event handler to its own base class events?
If I do add an event handler, I assume that it should be explicitly removed when the control is disposed. However, I am not sure how best to do this in the case of RichTextBox as WPF control classes do not seem to have any mechanism for detecting disposal.
Any suggestions please?
Thanks,
Tim
Of course, you can handle events of the base class. It's commonly done for the Loaded event, for instance, since there is no OnLoaded method.
You don't need to worry about removing the handler: since the event publisher and subscriber are the same instance, not removing the handler won't prevent the GC from collecting your object.