Introduction
Oh no!!!!! Is this yet another component, one of the literally dozens that are out there, that claims to provide VS.NET style menus? Well, yes, but please don't go away just yet, for I would like to quickly present you with some facts about this component that perhaps may lead you to think about trying it out.
First and foremost, this component provides a menu style that is 90% identical to the one provided by VS.NET (the 10% gap is described below in the "Work pending" section, please help me eliminate it). This means that it applies the VS.NET menu style to not only individual menu items but also to the client application's menu system as a whole. Specifically, the windows on which menu items are displayed are formatted in such a way that they look almost identical to the ones VS.NET uses to host its menu items. There are other free .NET components out there that do this as well, although very few in number. However, none of the ones I've come across, and boy have I come across many, address ALL the points that follow.
Second, the VSNetMenuProvider
component will provide your applications with VS.NET style menus on any platform the .NET runtime supports, that is, on any OS above and including W98, with one minor (or perhaps major in your view) exception I will discuss below in the "Work pending" section (please help me resolve it). The list includes flat borders and shadows, something other components I've looked at fail to provide as soon as they are run on systems other than WXP, and some do not work on WXP with themes enabled. Obviously, if your application only runs on WXP, then it does not matter at all whether the component works on other systems. In my case, however, W98 compatibility was not an option but rather a must. Therefore, for those of you who want VS.NET style menus that work on any .NET compliant OS without having to write more than two lines of code or purchase a huge and often times slow third party control, then perhaps you may want to give this component a try.
Third, the VSNetMenuProvider
component, as its name implies, is implemented as an IExtenderProvider
component. I will briefly discuss what this means, for those of you who do not already know, in the section below titled "Using VSNetMenuProvider". For now, however, be assured that in order to get full VS.NET style menus in your applications, only two lines of code are required, one when your application begins and another when it ends. Furthermore, if you don't care about flat menu borders, shadows, etc..., and only that the individual menu items are drawn with VS.NET menu styles applied, then these two lines of code are not necessary, although if this is the case, then there's really no difference between this component and the many others that are out there.
Fourth and in relation to the first point mentioned, although of less importance, this component mimics the not so obvious features of VS.NET menus, something other components do not do. This is a special treat for those of you, like me, who just adore the VS.NET menu style and want it in its entirety. For example, open the VS.NET IDE, click on any top level menu item on the menu bar, hit the escape key, and notice that the selected menu is displayed in a highlighted state as soon as the window hosting its sub menus disappears. Or, as another example, open the VS.NET IDE, position the main window so that the top level File menu is partially or completely hidden, click on the File menu either with the mouse or via its hotkey, and notice that the window hosting its sub menus paints its borders taking into account the visibility of the parent File menu item. Well, the VSNetMenuProvider
component does all of this and more in order to make your menus look almost exactly like the ones VS.NET has.
Fifth, final, and of very, very low importance, the VSNetMenuProvider
component is written in VB, not C#, as opposed to almost all of the other components out there. Below, in the section titled "Acknowledgements" I am going to give thanks to each and every individual whose code made this component a reality, the majority of which belong to derivatives of the C family (C#, C++).
Using VSNetMenuProvider
The VSNetMenuProvider
component implements the IExtenderProvider
interface, and as such extends the property list of other predetermined types. In this particular case the predetermined type is the MenuItem
. Specifically, each MenuItem
on a Form
that has a VSNetMenuProvider
member will be provided an ImageIndex
property. The VSNetMenuProvider
has an ImageList
property, and if set, will be used as the source of the the ImageIndex
property provided to each and every MenuItem
.
In order to use the component, open up a Windows project that has at least one Form
, which in turn should have at least one MainMenu
or ContextMenu
setup with menu items and at least one ImageList
with images, and then do the following:
- Add a reference to the
vbWindowsUI
assembly, which hosts the VSNetMenuProvider
component, and customize the Toolbox to include the controls exposed by the referenced assembly.
- Add a
VSNetMenuProvider
instance to a Form
and set its ImageList
property to any ImageList
member of the Form
.
- Select a
MenuItem
that will be assigned an image. The Properties Window will display an ImageIndex
property for the selected MenuItem
. Edit this property by selecting an image from the list of images displayed by the drop down editor or select the entry "(none)" to disassociate the selected MenuItem
from any particular image.
The following steps are required only if you want VS.NET menu styles (flat borders, shadows, etc...) applied to the form's menu system and not just to the individual menu items. There's a big difference!!!!!!!
- Add the following line of code in one, and only one, spot of the entire application right before any windows are displayed. Usually this spot will be in the
Load
event of your application's startup Form
or in Sub Main
.
vbWindowsUI.VSNetMenuProvider.Hook(Me)
- Add the following line of code in one, and only one, spot of the entire application right before it ends. Usually this spot will be in the
Closing
event of the last Form
displayed or in Sub Main
.
vbWindowsUI.VSNetMenuProvider.Unhook()
That's all you need to do in order to add complete VS.NET menu style support to your applications. Keep in mind that the above lines of code should be run at the application level, not at the Form
level. That is, you should not call these methods for each and every Form
your application displays, but rather one at the start (Hook
) and one at the end (Unhook
) of your application, regardless of how many forms with VS.NET style menus are displayed. Also, the VS.NET menu style is not applied at design time, only at runtime.
VSNetMenuProvider implementation
I'm not going to go into the implementation details of VSNetMenuProvider
, for the code is there at your disposal. Suffice to say that the nuts and bolts of the functionality is done via owner drawn menus, hooks, and subclassing. If you have any questions, please let me know.
Final VSNetMenuProvider notes
No special handling is required for menu items that will not have an image displayed along side of them. As long as the form has a VSNetMenuProvider
member, all of its menu items will be painted with VS.NET menu styles applied.
If you set to False
the OwnerDrawn
property of a menu item, it will not be painted with VS.NET styles applied, regardless of whether or not the parent form has a VSNetMenuProvider
member.
One, and only one, VSNetMenuProvider
instance is supported per form. I don't know what will happen at runtime if you try to add more than one instance to any particular form; therefore, I do not recommend that you try doing so. As I mention below in the "Work pending" section, VSNetMenuProvider
is missing logic to make sure that only one instance exists per form.
If you create menu items at run time, make sure you call the SetImageIndex
method of the VSNetMenuProvider
member that belongs to the form that will host the newly created menu items, with one call for each. Otherwise, they will not be painted with VS.NET menu styles applied. Furthermore, there's no need for you to programmatically set to True
the OwnerDrawn
property of the newly created menu items before or after calling SetImageIndex
. Also, if no image will be associated with any or all of the newly created menu items, pass in the value of -1 as the second argument. Otherwise, pass in the index value of the image to be displayed.
If you call the shared Hook
method of VSNetMenuProvider
in order to fully apply VS.NET menu styles to your menus, each and every form that your application displays will have flat menu windows, regardless of whether or not the form in question has a VSNetMenuProvider
member.
If you call the shared Hook
method of VSNetMenuProvider
in order to fully apply VS.NET menu styles to your menus, make sure you call the shared method Unhook
before the application ends.
The vbWindowUI
assembly that hosts the VSNetMenuProvider
type also exposes other control types that try to mimic the VS.NET UI look. You are free to use these controls as well.
Work pending
What follows is a list of tasks that need to be completed. The list is ordered from top to bottom with respect to each task's importance level.
- Top level menu items are not highlighted as the mouse hovers over them on OS versions below W2K. I've noticed that the
DrawItem
event for top level menu items is not raised when the mouse hovers over them. I need some ideas on how to get around this. Once this issue is resolved, VSNetMenuProvider
will function identically across all .NET compliant operating systems.
VSNetMenuProvider
needs to be implemented as a single instance component at the form level. Currently, more than one VSNetMenuProvider
instance can be added to a single form, a fact that should not be the case. One, and only one, instance should be allowed per form. I need some clues as to how to limit the number of VSNetMenuProvider
instances to only one per form, with this limit applied at design time, not at runtime. In other words, as soon as the developer tries to drop a VSNetMenuProvider
instance onto a form's component tray, a check will take place to make sure that no other instances exist. If the check fails, then the attempt to add the new instance will fail with a message informing the developer that a VSNetMenuProvider
instance already exists and, therefore, a new one cannot be added. If the check passes, then the new component instance is added without any errors. I know that this, like all other things, can in fact be done, for I have seen third party components that do this.
- Currently, the arrow that is displayed along side parent menu items changes colors from black to white as soon as it is selected, something that does not happen to VS.NET parent menu items. I need some ideas on how to prevent the arrow from changing colors. I tried painting over it; however, it seems that either the area of the arrow is clipped out of the drawing surface or the OS is painting the arrow after I paint the menu item, with the end result being that the arrow still changes colors.
- It seems that the
MeasureItem
event of owner drawn menus is raised only once during the menu's lifetime, right before it is displayed for the very first time. Obviously, this presents a problem if you decide to change the text of a menu item after its MeasureItem
event is called and also if the new text length is significantly longer than the text length that was used originally to measure the width of the menu item. I need some help on how to get around this issue without having to create a new menu item each time the old menu item's text needs to be changed.
- The component needs more testing on various flavors of .NET compliant operating systems. Experience has told me that just because we both have the same OS installed does not mean that what works on mine will work on yours. Thus far, I have tested the component on W98, W98SE, WNTSP6, W2K, and WXP with and without themes applied. WME and W.NET are pending questions.
- The shadow I'm drawing on operating systems that do not do this on my behalf could use some work.
- Testing is required for MDI applications. I have not tested the component in MDI applications.
- Sometime in the near future, I would to add a
Style
property to VSNetMenuProvider
, the value of which will determine exactly how the menus are painted. This property will likely take the form of an enumeration, with possible values being OfficeXP
, Office2003
, VSNet
, etc...
- I have not added any error handling code to the assembly; therefore, it is not ready for production. I will do so as soon as I am 100% certain that the types it exposes work properly on all .NET compliant operating systems.
Please help me complete this work, especially the first 7 tasks. I really don't expect anyone to help me with the last 2 tasks. Furthermore, I'm sure all of you will eventually have items to add to this list.
Acknowledgements
VSNetMenuProvider
would never have been possible without the help, usually in the form of code, of the following developers:
- Georgi Atanasov (C family): his implementation of
FlatMenusForm
provided me with the techniques required to flatten the borders of menu windows. I translated and used lots of his Win32 API code. Thank you!!!!!!
- Chris Beckett (C family): he gave me the idea for implementing
VSNetMenuProvider
as an IExtenderProvider
component. Thank you!!!!!
- Jean-Michel LE FOL (C family): his implementation of
CWndMenuXP
showed me how to process the WM_NCCALCSIZE
and WM_WINDOWPOSCHANGING
messages in order to draw shadows around menu windows on operating systems that do not do this automatically. Thank you!!!!!!!
- Bruno Podetti (C family): his implementation of
CNewMenu
showed me how to subclass the "special menu window", one that is never created nor ever destroyed and which is used by various systems other than WXP. Thank you!!!!!!!
- Steve Yam (C family): his implementation of
ExtImageIndexEditor
showed me how to create a custom, developer friendly editor for the ImageIndex
property provided by VSNetMenuProvider
to objects of type MenuItem
. Thank you!!!!!!
- Carlos H. Perez (C family): his implementation of
VSNetComboBox
showed me how easy it is to change the UI look of basic controls, simply by processing the WM_PAINT
message. As mentioned above in the "Final VSNetMenuProvider notes" section, the vbWindowsUI
assembly that hosts the VSNetMenuProvider
type also exposes various other VS.NET style controls, most of which employ this developer's techniques to change the UI look. Thank you!!!!!!!
- Andr�s Pons (VB): although I was never able to get a hold of the source code of his
SmartMenuXP
OCX, his writings about it nevertheless guided me in the right direction. However, his main contribution came in the form of inspiration, given the fact that his VB 6 component mimics the OfficeXP style identically (99.99%), down to the very last detail. Had this not been the case, I may have decided to not take the time to implement the not so obvious VS.NET menu style features. Also, of course, the fact that SmartMenuXP
was written in VB pushed me even further. Thank you!!!!!!
Contact me
My name is Giancarlo Aguilera, and you can contact me by email. My email addresses are:
- giancarlo.aguilera@intercept.net and
- giancarloaguilera@hotmail.com.