Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Flat VS.NET Style Menus - Even on Windows 98

0.00/5 (No votes)
3 Sep 2003 1  
Flat VS.NET style menus - Even on Windows 98

Sample Image - maximum width is 600 pixels

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:

  1. 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.
  2. Add a VSNetMenuProvider instance to a Form and set its ImageList property to any ImageList member of the Form.
  3. 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!!!!!!!

  4. 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.
    'shared (static) method of VSNetMenuProvider
    
    vbWindowsUI.VSNetMenuProvider.Hook(Me)
  5. 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.
    'shared (static) method of VSNetMenuProvider
    
    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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. The shadow I'm drawing on operating systems that do not do this on my behalf could use some work.
  7. Testing is required for MDI applications. I have not tested the component in MDI applications.
  8. 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...
  9. 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:

  1. 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!!!!!!
  2. Chris Beckett (C family): he gave me the idea for implementing VSNetMenuProvider as an IExtenderProvider component. Thank you!!!!!
  3. 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!!!!!!!
  4. 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!!!!!!!
  5. 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!!!!!!
  6. 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!!!!!!!
  7. 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:

  1. giancarlo.aguilera@intercept.net and
  2. giancarloaguilera@hotmail.com.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here