using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; //VistaMenu v1.7, created by Wyatt O'Day //Visit: http://wyday.com/vistamenu/ namespace wyDay.Controls { //Properties for the MenuItem internal class Properties { public Image Image; public IntPtr renderBmpHbitmap = IntPtr.Zero; public Bitmap PreVistaBitmap; } //enum WindowsType { VistaOrLater, XP, PreXP } [ProvideProperty("Image", typeof(MenuItem))] public partial class VistaMenu : Component, IExtenderProvider, ISupportInitialize { private Container components; private readonly Hashtable properties = new Hashtable(); private readonly Hashtable menuParents = new Hashtable(); private bool formHasBeenIntialized; private bool isVistaOrLater; // performance hacks private Queue> _pendingSetImageCalls = new Queue>(); private static bool _firstVistaMenu = true; private bool _delaySetImageCalls = false; public bool DelaySetImageCalls { get { return _delaySetImageCalls; } set { _delaySetImageCalls = value; } } #region Imports [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern bool SetMenuItemInfo(HandleRef hMenu, int uItem, bool fByPosition, MENUITEMINFO_T_RW lpmii); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern bool SetMenuInfo(HandleRef hMenu, MENUINFO lpcmi); [DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); #endregion public VistaMenu() { isVistaOrLater = Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6; InitializeComponent(); } public VistaMenu(IContainer container) : this() { container.Add(this); if (_firstVistaMenu) { _delaySetImageCalls = true; _firstVistaMenu = false; } } /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { components = new Container(); } /// /// Clean up any resources being used. /// protected override void Dispose(bool disposing) { if (disposing) { //release all the HBitmap handles created foreach (DictionaryEntry de in properties) { if (((Properties)de.Value).renderBmpHbitmap != IntPtr.Zero) DeleteObject(((Properties)de.Value).renderBmpHbitmap); if (((Properties)de.Value).PreVistaBitmap != null) ((Properties)de.Value).PreVistaBitmap.Dispose(); } if (components != null) { components.Dispose(); } } base.Dispose(disposing); } bool IExtenderProvider.CanExtend(object o) { if (o is MenuItem) { // reject the menuitem if it's a top level element on a MainMenu bar if (((MenuItem)o).Parent != null) return ((MenuItem)o).Parent.GetType() != typeof(MainMenu); // parent is null - meaning it's a context menu return true; } if (o is Form) return true; return false; } private Properties EnsurePropertiesExists(MenuItem key) { Properties p = (Properties)properties[key]; if (p == null) { p = new Properties(); properties[key] = p; } return p; } #region MenuItem.Image [DefaultValue(null)] [Description("The Image for the MenuItem")] [Category("Appearance")] public Image GetImage(MenuItem mnuItem) { return EnsurePropertiesExists(mnuItem).Image; } [DefaultValue(null)] public void SetImage(MenuItem mnuItem, Image value) { this.SetImage(mnuItem, value, false); } public void SetImage(MenuItem mnuItem, Image value, bool ignorePending) { if (_delaySetImageCalls && !ignorePending) { _pendingSetImageCalls.Enqueue(new KeyValuePair(mnuItem, value)); return; } Properties prop = EnsurePropertiesExists(mnuItem); if (DesignMode) prop.Image = value; if (!DesignMode && isVistaOrLater) { //Destroy old bitmap object if (prop.renderBmpHbitmap != IntPtr.Zero) { DeleteObject(prop.renderBmpHbitmap); prop.renderBmpHbitmap = IntPtr.Zero; } //if there's no Image, then just bail out if (value == null) { // wj32: clean up resources before doing that... RemoveVistaMenuItem(mnuItem); return; } //convert to 32bppPArgb (the 'P' means The red, green, and blue components are premultiplied, according to the alpha component.) Bitmap renderBmp = new Bitmap(value.Width, value.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); Graphics g = Graphics.FromImage(renderBmp); g.DrawImage(value, 0, 0, value.Width, value.Height); g.Dispose(); prop.renderBmpHbitmap = renderBmp.GetHbitmap(Color.FromArgb(0, 0, 0, 0)); renderBmp.Dispose(); if (formHasBeenIntialized) { AddVistaMenuItem(mnuItem); } } else if (!DesignMode && !isVistaOrLater) { if (prop.PreVistaBitmap != null) { prop.PreVistaBitmap.Dispose(); prop.PreVistaBitmap = null; } if (value == null) { RemoveVistaMenuItem(mnuItem); return; } Bitmap bmp = new Bitmap(value.Width, value.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); Graphics g = Graphics.FromImage(bmp); g.DrawImage(value, 0, 0, value.Width, value.Height); g.Dispose(); prop.PreVistaBitmap = bmp; //for every Pre-Vista Windows, add the parent of the menu item to the list of parents if (formHasBeenIntialized) { AddPreVistaMenuItem(mnuItem); } } } public void PerformPendingSetImageCalls() { while (_pendingSetImageCalls.Count > 0) { var call = _pendingSetImageCalls.Dequeue(); this.SetImage(call.Key, call.Value, true); } } #endregion void ISupportInitialize.BeginInit() { } readonly MENUINFO mnuInfo = new MENUINFO(); void AddVistaMenuItem(MenuItem mnuItem) { //get the bitmap children of the parent List mnuBitmapChildren = (List)menuParents[mnuItem.Parent.Handle]; if (mnuBitmapChildren == null) { if (mnuItem.Parent.GetType() == typeof(ContextMenu)) ((ContextMenu)mnuItem.Parent).Popup += MenuItem_Popup; else ((MenuItem)mnuItem.Parent).Popup += MenuItem_Popup; //intialize all the topmost menus to be of type "MNS_CHECKORBMP" (for Vista classic theme) SetMenuInfo(new HandleRef(null, mnuItem.Parent.Handle), mnuInfo); mnuBitmapChildren = new List { mnuItem }; //set the new children list to the corresponding parent menuParents[mnuItem.Parent.Handle] = mnuBitmapChildren; } else { mnuBitmapChildren.Add(mnuItem); } } void AddPreVistaMenuItem(MenuItem mnuItem) { if (menuParents[mnuItem.Parent] == null) { menuParents[mnuItem.Parent] = true; if (formHasBeenIntialized) { //add all the menu items with custom paint events foreach (MenuItem menu in mnuItem.Parent.MenuItems) { menu.DrawItem += MenuItem_DrawItem; menu.MeasureItem += MenuItem_MeasureItem; menu.OwnerDraw = true; } } } } public void RemoveVistaMenuItem(MenuItem mnuItem) { if (menuParents[mnuItem.Parent.Handle] != null) { List mnuBitmapChildren = (List)menuParents[mnuItem.Parent.Handle]; mnuBitmapChildren.Remove(mnuItem); } } public void RemovePreVistaMenuItem(MenuItem mnuItem) { mnuItem.DrawItem -= MenuItem_DrawItem; mnuItem.MeasureItem -= MenuItem_MeasureItem; mnuItem.OwnerDraw = false; } void ISupportInitialize.EndInit() { if (!DesignMode) { if (isVistaOrLater) { foreach (DictionaryEntry de in properties) { AddVistaMenuItem((MenuItem)de.Key); } } else // Pre-Vista menus { if (ownerForm != null) ownerForm.ChangeUICues += ownerForm_ChangeUICues; foreach (DictionaryEntry de in properties) { AddPreVistaMenuItem((MenuItem)de.Key); } //add event handle for each menu item's measure & draw routines foreach (DictionaryEntry parent in menuParents) { foreach (MenuItem mnuItem in ((Menu)parent.Key).MenuItems) { mnuItem.DrawItem += MenuItem_DrawItem; mnuItem.MeasureItem += MenuItem_MeasureItem; mnuItem.OwnerDraw = true; } } } formHasBeenIntialized = true; } } void MenuItem_Popup(object sender, EventArgs e) { //get the parentHandle IntPtr parentHandle = ((Menu)sender).Handle; //get the list of children menuitems to "refresh" List mnuBitmapChildren = (List)menuParents[parentHandle]; MENUITEMINFO_T_RW menuItemInfo = new MENUITEMINFO_T_RW(); foreach (MenuItem menuItem in mnuBitmapChildren) { //menuItem. menuItemInfo.hbmpItem = ((Properties)properties[menuItem]).renderBmpHbitmap; //refresh the menu item SetMenuItemInfo(new HandleRef(null, parentHandle), (int)typeof(MenuItem).InvokeMember("MenuID", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty, null, menuItem, null), false, menuItemInfo); } } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class MENUITEMINFO_T_RW { public int cbSize = Marshal.SizeOf(typeof(MENUITEMINFO_T_RW)); public int fMask = 0x00000080; //MIIM_BITMAP = 0x00000080 public int fType; public int fState; public int wID; public IntPtr hSubMenu = IntPtr.Zero; public IntPtr hbmpChecked = IntPtr.Zero; public IntPtr hbmpUnchecked = IntPtr.Zero; public IntPtr dwItemData = IntPtr.Zero; public IntPtr dwTypeData = IntPtr.Zero; public int cch; public IntPtr hbmpItem = IntPtr.Zero; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class MENUINFO { public int cbSize = Marshal.SizeOf(typeof(MENUINFO)); public int fMask = 0x00000010; //MIM_STYLE; public int dwStyle = 0x04000000; //MNS_CHECKORBMP; public uint cyMax; public IntPtr hbrBack = IntPtr.Zero; public int dwContextHelpID; public IntPtr dwMenuData = IntPtr.Zero; } }