Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

How to get events assigned to ToolStripMenuItem.Click

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Academia - 28 Feb 2008 01:39 GMT
In the code below I

Call CopyToolStripMenuItem which effectively copies the properties of
saveToolStripMenuItem one at a time into newItem.

The next statement changes the Text of newItem to "New"

The next statement adds newItem to the DropDownItems.

Finally I assign an event to newItem.Click.

But what I really want to assign to newItem.Click is whatever is assigned to
saveToolStripMenuItem.Click

Do you know how to do that?

Thanks

ToolStripMenuItem newItem;

newItem = CloneToolStripMenuItem(saveToolStripMenuItem);

newItem.Text="New";

this.fileToolStripMenuItem.DropDownItems.AddRange(new
System.Windows.Forms.ToolStripItem[] {

newItem});

newItem.Click += new System.EventHandler(this.newToolStripMenuItem_Click);
Peter Duniho - 28 Feb 2008 02:52 GMT
> In the code below I
>
[quoted text clipped - 12 lines]
>
> Do you know how to do that?

As far as I know, the only way to do that is to subclass ToolStripItem (or  
ToolStripMenuItem) and provide a property or method that exposes that  
information.  Even then, you would have to implement a new event, rather  
than using the existing Click event, as the auto-generated field used by  
the Click event would be private and inaccessible to your sub-class.

Of course, if you do make a sub-class, you could implement a Clone()  
method too, which would make that "CloneToolStripMenuItem()" method a  
little nicer to use.  :)

For what it's worth, you could track the subscribers elsewhere.  For  
example, you could use the Tag property to keep track of who's subscribed  
to the event.  It'd be a pain, and would be fragile, as you'd have to make  
sure you always updated the property any time you subscribed or  
unsubscribed from the Click event.  But it could be done.

I think it's kind of too bad that the events in the Control class aren't  
virtual.  It would be nice to be able to override their default behavior,  
for situations like this.

Pete
Academia - 28 Feb 2008 08:06 GMT
>> In the code below I
>>
[quoted text clipped - 16 lines]
> ToolStripMenuItem) and provide a property or method that exposes that
> information.

I'm not going to try that - I don't think I'd be sccessful.  But I'm
curious. If I did, could I get the Designer to create a menu using it?

Thanks for the info

>Even then, you would have to implement a new event, rather  than using the
>existing Click event, as the auto-generated field used by  the Click event
[quoted text clipped - 15 lines]
>
> Pete
Peter Duniho - 28 Feb 2008 16:25 GMT
>> As far as I know, the only way to do that is to subclass ToolStripItem  
>> (or
>> ToolStripMenuItem) and provide a property or method that exposes that
>> information.
>
> I'm not going to try that - I don't think I'd be sccessful.

Why not?  It should be relatively simple.  The basic steps are:

    * Sub-class the class
    * Add a new event (e.g. "public event CloneableClick")
    * Add an override for the OnClick() method in which you raise the  
CloneableClick event
    * Add a public property that returns the current value of the  
CloneableClick delegate
    * Subscribe click handlers to the CloneableClick event instead of the  
Click event

Optional:
    * Add a Clone() method, implementing ICloneable, in which you do all  
the stuff you would have otherwise done in CloneToolStripMenuItem()
    * Hide the base class Click event with a private version.  This won't  
stop any code from casting up to the base class and getting at it that  
way, but it should at least prevent it from being seen in the designer and  
wherever you're using your own class explicitly.

I'm not familiar enough with ToolStripItem and its descendants, but it's  
_possible_ that you could simply do the last step, and use  
Object.MemberwiseClone() to do the cloning instead of handling everything  
explicitly.  I'm a little worried that the base classes, like  
MarshalByRefObject and Component might have some unmanaged stuff that  
can't be safely duplicated that way, but it might be worth a try.  The  
worst that could happen is that it wouldn't work and you're no worse off  
than before, and the best that could happen is that it would work, and  
then you're done.  :)

I think the above would be more more maintainable than trying to track the  
subscribers somewhere else.

> But I'm
> curious. If I did, could I get the Designer to create a menu using it?

I don't know.  For regular custom controls, those show up in the Toolbox  
as soon as they are compiled and valid within the current project (i.e.  
they show up when you're looking at the same project in which they are  
defined, as well as when you're looking at any project that references  
that assembly).

But menu items aren't usually "drag-and-drop" into the menu, and I don't  
recall off the top of my head whether a custom ToolStripMenuItem shows up  
anywhere accessible that would allow you to put it in the menu using the  
Designer.

You could always try it and see though.  :)  Just to test that you  
wouldn't have to write any code...just create the sub-class without adding  
anything and see if you can then design a menu that uses it.

Pete
Academia - 28 Feb 2008 23:52 GMT
>>> As far as I know, the only way to do that is to subclass ToolStripItem
>>> (or
[quoted text clipped - 9 lines]
>     * Add an override for the OnClick() method in which you raise the
> CloneableClick event

Thanks for so much info.
I'm new at c# and don't know how to do the next item

>     * Add a public property that returns the current value of the
> CloneableClick delegate
[quoted text clipped - 41 lines]
>
> Pete
Peter Duniho - 29 Feb 2008 06:02 GMT
> [...]
> Thanks for so much info.
> I'm new at c# and don't know how to do the next item
>
>>     * Add a public property that returns the current value of the
>> CloneableClick delegate

In the class where the event is declared, the delegate is available using  
the same name as the event.  In other words, if you have declared an event  
named "MyEvent" in class "MyClass", when you access MyClass.MyEvent from  
outside the class, you get the event, but when you access it from within  
the class, you get the delegate field itself.

Jon Skeet's got a good article that you should find informative, with  
respect to how events and delegates work together.  You can find it here:  
http://www.yoda.arachsys.com/csharp/events.html

Very briefly, doing what I describe above might look something like this:

class MyClass
{
    public event EventHandler CloneableClick;

    public EventHandler CloneableClickSubcribers
    {
        get { return CloneableClick; }
    }
}

Pete
Academia - 01 Mar 2008 15:57 GMT
Great article but one reading is not enough! I'll read it again. But I don't
think my answer is in there (although it might be.)

I believe you showed me how to create a clone that exposes it's click
EventHandler. Good stuff!

I make a MenuStrip using the Ide.  I want to clone one of the
ToolStripMenuItems. To do that I need to get the  EventHandler delegate it
has stored for click.  But I can't seem to do that. If you know how to do
that I'd appreciate learning how -  I've been trying for a couple of days
now.

Thanks for the help

>> [...]
>> Thanks for so much info.
[quoted text clipped - 26 lines]
>
> Pete
Peter Duniho - 01 Mar 2008 17:54 GMT
> [...]
> I make a MenuStrip using the Ide.  I want to clone one of the
> ToolStripMenuItems. To do that I need to get the EventHandler delegate it
> has stored for click.  But I can't seem to do that. If you know how to do
> that I'd appreciate learning how -  I've been trying for a couple of days
> now.

That depends on what you mean.  If you want the delegate stored for the  
Click event, there is no direct way to do that.  It's private to the class  
that declares the event, which means that if you can't modify the actual  
class that declares the event, there's no way to get at the value via  
normal C# syntax.  And of course, you can't modify the ToolStripMenuItem  
class.

You _might_ be able to do it via reflection (see  
http://msdn2.microsoft.com/en-us/library/system.reflection.aspx), which  
doesn't do anything to prevent you from accessing private members.  
However, even assuming it works IMHO that would be a poor solution.  It's  
less efficient, and more importantly it obscures the relatively abnormal  
use of the Click event by hiding it in whatever code does the reflection.  
It's more awkward to create a subclass that exposes a new event that you  
can clone, or to create a subclass that has a separate method for  
subscribing to the Click event in which you make a copy of the delegate  
passed in, or any of the other techniques you might use to do this.

But in my own personal opinion, that awkwardness is actually a good thing,  
as it highlights the unusual nature of the implementation.

As far as using reflection goes, keep in mind also that doing so would be  
very fragile.  You'd be relying on a specific implementation, which could  
change with any revision of .NET.  If and when it does, your own code will  
break.

I don't know how the ToolStripMenuItem deals with its events, but it's my  
recollection that the Control class does _not_ use the default C# event  
declarations.  Instead, it implements its own add/remove methods for each  
event, and stores event subscribers in a separate data structure (probably  
a dictionary or something like that).  It does this because in the vast  
majority of cases, almost every event remains unsubscribed for the entire  
lifetime of the control, and so not having a separate field for every  
single event saves a lot of memory.

If the ToolStripMenuItem handles events like this now, then you're already  
in for an uphill battle hooking into that code (you'll probably want to  
use Reflector or something similar to figure out how the events are being  
stored).  And even if it doesn't do this now, it could in the future.  
Either way, with every revision of .NET, you run the risk of your code no  
longer working with the new implementation of ToolStripMenuItem.

A number of non-reflective solutions have been posted in this thread, and  
there are plenty of variations on those themes.  None will do _exactly_  
what you're asking, but all will accomplish very much the same thing.  I  
strongly recommend you stick with one of these alternative approaches, and  
forget about trying to get at the actual value of the Click delegate in  
the ToolStripMenuItem class itself.  It's a private member and for better  
or worse, you're not supposed to have it.

Pete
Paul E Collins - 28 Feb 2008 11:03 GMT
> Finally I assign an event to newItem.Click. But what I really want to
> assign to newItem.Click is whatever is assigned to
> saveToolStripMenuItem.Click

You can cause the New button to send a simulated mouse click to the Save
button, i.e.

void newItem_Click(object sender, EventArgs e)
{
 saveToolStripMenuItem.PerformClick();
}

Eq.
Academia - 29 Feb 2008 13:28 GMT
I did try this it works and is a straightforward way. There have been times
when I've called (in vb) the event handler routine directly - this is
better. Can't miss understanding what is intended.

Thanks

>> Finally I assign an event to newItem.Click. But what I really want to
>> assign to newItem.Click is whatever is assigned to
[quoted text clipped - 9 lines]
>
> Eq.
Peter Duniho - 29 Feb 2008 18:53 GMT
> I did try this it works and is a straightforward way. There have been  
> times
> when I've called (in vb) the event handler routine directly - this is
> better. Can't miss understanding what is intended.

Yes, if that was your actual intent, I agree that's a better way to  
approach the question.

Note, of course, that that's not the question you actually asked.

Pete
Academia - 29 Feb 2008 20:26 GMT
>> I did try this it works and is a straightforward way.

Below I was just remarking about an additional usage.
>>There have been  times
>> when I've called (in vb) the event handler routine directly - this is
[quoted text clipped - 6 lines]
>
> Pete

thanks

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.