After researching various possibilities, I have found a solution that works
for me. I've written it up as a sort of tutorial so that if others are
confused about events and handlers it might help them understand the
concepts better.
Again, the original problem was "how to 'see' events generated from a
control in that control's parent container".
Example, let's say you have a usercontrol that has a button on it. If you
drag your usercontrol onto your form and create a "click" event handler for
it, your click event handler will get called if the user clicks on any part
of your usercontrol except the button. If the user clicks on the button in
the usercontrol your click event handler does not get called.
Problem analysis:
Each control has it's own 'click' event defined. The click event for the
usercontrol is different and distinct from the click event of the button in
the usercontrol. So what you want to do is somehow create a new 'click'
event that both the usercontrol and button can use.
There are a couple of ways to handle this. both involve defining a click
event in your usercontrol that will be shared by the usercontrol and the
button. Let's look at the more involved way first, then I'll show you an
easier way.
The tool that we will use in our solution is the RAISEEVENT statement. This
statement will raise an event that has been defined in your module. The
tricky thing is that you CAN'T use raiseevent to raise an event that has
been defined outside of your module. So, for example, you can't use
raiseevent to raise a standard 'click' event because you have not defined
the standard click event in your module. We need to define our own event.
So, lets start by going into our usercontrol module and defining an event.
Now, even though it's your event and you can define as many or as few
parameters as you wish, let's create an event that has the same number and
type of parameters as the standard "click" event (you'll find out why
later).
Public Event MyClick(ByVal sender As System.Object, ByVal e As
System.EventArgs)
Now that we have defined an event in our usercontrol, we can use the
raiseevent statement to raise that event. But where do we raise it? Just
because we created our own event doesn't mean that the standard system
events are gone. they are still there and we can use them. Specifically, we
need to create a sub for both the click event of the usercontrol and the
click event of the button on the usercontrol and that's where we put our
raiseevent statement.
The click event for the usercontrol will look something like this:
Private Sub UserControl_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyBase.Click
RaiseEvent MyClick(sender, e)
End Sub
And the click event for the button (assume it's name is Button1) will look
like this:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
RaiseEvent MyClick(sender, e)
End Sub
Now, what happens to the MyClick event when it's raised? It looks for
something to tell it where to go. That something is called a Delegate. A
Delegate links the event to the routine that will handle it. Now, if you've
used VB.Net at all you have probably used delegates without realizing it.
As a matter of fact, you used one already. The "Handles" clause above is a
delegate that links the "Button1.Click" event to the "Button1_Click" sub.
The "Handles" is one form of specifying a delegate. Although it doesn't
apply to our example, another way (at runtime) to specify (or change) a
delegate is by using an ADDHANDLER statement, like this:
AddHandler Button1.Click, AddressOf Button1_Click1
Back to our story. we need to go into our form where the usercontrol is
being used, and create something to handle our 'MyClick" event. So we will
need code like this:
Private Sub MyButton1_Click1(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyButton1.MyClick
MsgBox("I clicked MyButton1 on the main form")
End Sub
In the above example MyButton1 is the name we gave to the instance of the
usercontrol we dragged onto the form. So MyButton1 is an instance of
"usercontrol".
When the usercontrol class raises the event, that event is raised on every
class that has elected to handle events for that instance of the object.
The "Handles MyButton1.MyClick" tells the system that this routine will
handle the event raised in the "usercontrol" class instance called
"MyButton1". Wow, I hope that's clear..
We could stop here and everything above would work fine, but I promised you
an easier way to do the same thing.
Remember that to get our example to work we created a custom event in the
usercontrol class, and added a "handler" or "delegate" in our main form to
handle that event. We can cut our work in half by creating an event in our
usercontrol that has THE SAME NAME as an event that our main form already
knows about. What could we call the event??? How about "Click". Our main
form already knows about the "click" event, and by naming our own event
"click" we wouldn't have to make any changes in the main form. We would
just handle the "click" event in the main form as we normally would.
Therefore, the only changes we need would be in our usercontrol code. So,
how exactly do you create your own event that has the same name as an
already existing event? The answer is that you have to use the SHADOWS
keyword when defining your event. When an event is declared with the
shadows keyword it "hides" the existing event and, your custom event sort of
takes over for that original event. Here's how it looks in code. Put this
in the usercontrol class outside of any procedures.
Public Shadows Event Click(ByVal sender As System.Object, ByVal e As
System.EventArgs)
This statement takes the place of the "Public Event MyClick" that we used
before. Now that you are using "Click" as the name of your event instead of
"MyClick" the RaiseEvent statement has to look like this:
RaiseEvent Click(sender, e)
Remember that, like the previous example, the RaiseEvent statement is in the
event handlers for the "click" event for your usercontrol and the button on
your usercontrol. So why is this not a conflict? How does the system
differentiate between your click event and the normal click event? It's
actually easy. When you specify a handler for the click event in the normal
way (ie: ..handles button.click or handles mybase.click) the system
defaults to using the standard system event. But the RaiseEvent, by
definition, can only raise an event that was defined in the module it's in,
so the RaiseEvent can ONLY raise the click event that you defined.
By shadowing the system click event you eliminated the need to make any code
changes in the main form. This is why, in the previous example, I chose to
define my custom click event with the same number and type of parameters as
the standard system click event. I knew that I would eventually be
"replacing" the standard click event with my own and wanted the parameters
(ie: signature) to match. To continue the explanation, simply dragging your
usercontrol onto your main form and setting it's "click" event (as you would
normally do for any control) works just fine with your custom 'click' event,
and anywhere you click on your usercontrol, that usercontrol's click event
will be fired. Just what we wanted..
Hope this helps clear up the concept of events and how to handle them.
John