不是人啊!不是人!——.NET 风格菜单组件

xlfancy 2003-9-24 15:59:00

.NET 风格菜单组件。是哦所见最酷的一个。

没用一个 WIN32 API,纯 C# 搞定——主菜单、子菜单、快捷菜单的 .NET 菜单风格!

鬼佬真的是鬼呀,不是人!

哦花了两天时间,才搞明白这个组件的用法(别笑哦蠢啊,至少会用哈...)

此主题相关图片如下:

img_1

源码出处:.NET 风格菜单组件

//============.NET 风格菜单组件源码欣赏===============

/* ***************************************
 *           NiceMenu.cs (v 1.1)
 *  --------------------------------------
 *  This is an improvement by Francesco Natali (fn.varie@libero.it)
 *  over an improvement by Sajith M
 *  of the "Visual Studio .NET Menu Style"
 *  by Carlos H. Perez that appeared on CodeProject
 *  (http://www.codeproject.com)
 * ---------------------------------------
 *       How to use it
 *  You have to add:
 *  using Utility.NiceMenu;
 *  and after the "InitializeComponent()" code:
 *   NiceMenu myNiceMenu = new NiceMenu();
 *  myNiceMenu.UpdateMenu(this.nameofyourmainmenu, new NiceMenuClickEvent(nameofyourclickfunction));
 * ---------------------------------------
 *  With only three lines of code your menu
 *    will be AUTOMATICALLY updated with
 *       a new great .Net style menu!
 * ****************************************/
namespace Utility.NiceMenu
{
    using System;
    using System.Windows.Forms;
    using System.Drawing;
    using System.Drawing.Text;
    using System.Diagnostics;
    using System.Collections;

    /* ******************************************************
    *   NiceMenuClickEvent(object sender, System.EventArgs e)
    * ------------------------------------------------------
    * this is a delegate event that will be initializate with
    *  the user function to handle the click event on the menu
    * ******************************************************/
    public delegate void NiceMenuClickEvent(object sender, System.EventArgs e);
    /* ******************************************************
    *  This is a simple example of a function
    *  to handle the click event:  
    *  -----------------------------------------------------
    public void myClickMenuEvent(object sender, System.EventArgs e)
    {
        if (typeof(NiceMenu).IsInstanceOfType(sender))
        {
            NiceMenu item = (NiceMenu)sender;
            MessageBox.Show(item.Text); //example
        }
    }
    * ******************************************************/

    /// <summary>
    /// A great and free improvement menu class!
    /// </summary>
    public class NiceMenu : MenuItem
    {
        /* If you want to add some incons in your menu 
        * you have to:
        * 1. add an imagelist control in your form
        * 2. add your icons in your imagelist control
        * 3. add in your menu items the index of the icon 
        *    in the first two characters. For example:
        *    00New
        *    01Open
        *    02Close
        * 4. After the "NiceMenu myNiceMenu = new NiceMenu();" code
        *    you have to add this simple line of code: 
        *    myNiceMenu.MenuImages = nameofyourimagelist; 
        * */
        /// <summary>
        /// Set "MenuImages" with the your ImageList to add icons in your menu.
        /// </summary>
        public ImageList MenuImages;

        // NiceMenu array to save the main menu item
        // so at runtime i can search an item and modify it
        private static NiceMenu[] myModifyNiceMenu = new NiceMenu[20];
        private static NiceMenu[] myModifyContextNiceMenu = new NiceMenu[20];
        private static int contModify = 0;
        private static int contModifyContext = 0;

        private static Color window;
        // Some useful properties to change the color of your menu
        private static Color backcolor;
        private static Color barcolor;
        private static Color selectioncolor;
        private static Color framecolor;

        private static int iconSize = SystemInformation.SmallIconSize.Width + 5;
        private static int itemHeight;
        private static bool doColorUpdate = false;
        private string shortcuttext = "";
        private Image icon   = null;
        private static int BITMAP_SIZE = 16;
        private static int STRIPE_WIDTH = iconSize + 5;
  
        public NiceMenu() : base() 
        {
            OwnerDraw = true; UpdateColors();
        }
  
        private NiceMenu(string name) : base(name) 
        {
            OwnerDraw = true; UpdateColors();
        }
        // public for the AddRecentFile function
        public NiceMenu(string name, EventHandler handler) : base(name, handler) 
        {
            OwnerDraw = true; UpdateColors();
        }

        private NiceMenu(string name, NiceMenu[] items) : base(name, items) 
        {
            OwnerDraw = true; UpdateColors();
        }

        private NiceMenu(string name, EventHandler handler, Shortcut shortcut) : base(name, handler, shortcut) 
        {
            OwnerDraw = true; UpdateColors();
        }

        private NiceMenu(MenuMerge mergeType, int mergeOrder, Shortcut shortcut, string name, EventHandler onClick, EventHandler onPopup, EventHandler onSelect, NiceMenu[] items) : base(mergeType, mergeOrder, shortcut, name, onClick, onPopup, onSelect, items) 
        {
            OwnerDraw = true; UpdateColors();
        }

        private NiceMenu(string name, Image img) : this(name) 
        {
            icon = img;
        }

        private NiceMenu(string name, EventHandler handler, Image img) : this(name, handler) 
        {
            icon = img;
        }

        private NiceMenu(string name, EventHandler handler, Shortcut shortcut, Image img) : this(name, handler, shortcut) 
        {
            icon = img;
        }
 
        // *********************************************************

        private Image Icon 
        {
            get 
            { return icon; }
            set    
            { icon = value; }
        }

        private string ShortcutText 
        {
            get 
            { return shortcuttext; }
            set 
            { shortcuttext = value; }
        }

        public Color SelectionColor
        {
            get
            { return selectioncolor; }
            set
            { selectioncolor = value; }
        }

        public Color BackColor
        {
            get
            { return backcolor; }
            set
            { backcolor = value; }
        }

        public Color BarColor
        {
            get
            { return barcolor; }
            set
            { barcolor = value; }
        }

        public Color FrameColor
        {
            get
            { return framecolor; }
            set
            { framecolor = value; }
        }

        //************************************************************************

        private void UpdateColors()
        {
            window = SystemColors.Window;
            backcolor  = SystemColors.ControlLightLight;
            barcolor = SystemColors.Control;
            selectioncolor  = SystemColors.Highlight;
            framecolor = SystemColors.Highlight;

            int wa = (int)window.A;
            int wr = (int)window.R;
            int wg = (int)window.G;
            int wb = (int)window.B;

            int mna = (int)backcolor.A;
            int mnr = (int)backcolor.R;
            int mng = (int)backcolor.G;
            int mnb = (int)backcolor.B;

            int sta = (int)barcolor.A;
            int str = (int)barcolor.R;
            int stg = (int)barcolor.G;
            int stb = (int)barcolor.B;

            int sla = (int)selectioncolor.A;
            int slr = (int)selectioncolor.R;
            int slg = (int)selectioncolor.G;
            int slb = (int)selectioncolor.B;

            backcolor = Color.FromArgb(wr-(((wr-mnr)*2)/5), wg-(((wg-mng)*2)/5), wb-(((wb-mnb)*2)/5));
            barcolor = Color.FromArgb(wr-(((wr-str)*4)/5), wg-(((wg-stg)*4)/5), wb-(((wb-stb)*4)/5));
            selectioncolor = Color.FromArgb(wr-(((wr-slr)*2)/5), wg-(((wg-slg)*2)/5), wb-(((wb-slb)*2)/5));
        }

        private static void UpdateMenuColors()
        {            
            doColorUpdate = true;
        }

        private void DoUpdateMenuColors()
        {
            UpdateColors();
            doColorUpdate = false;
        }

        protected override void OnMeasureItem(MeasureItemEventArgs e) 
        {
            base.OnMeasureItem(e);

            if (Shortcut != Shortcut.None) 
            {
                string text = "";
                int key = (int)Shortcut;
                int ch = key & 0xFF;
                if (((int)Keys.Control & key) > 0) text += "Ctrl+";
                if (((int)Keys.Shift & key) > 0) text += "Shift+";
                if (((int)Keys.Alt & key) > 0) text += "Alt+";

                if (ch >= (int)Shortcut.F1 && ch <= (int)Shortcut.F12)
                    text += "F" + (ch - (int)Shortcut.F1 + 1);
                else 
                {
                    if ( Shortcut == Shortcut.Del) 
                    {
                        text += "Del";
                    }
                    else 
                    {
                        text += (char)ch;
                    }
                }
                shortcuttext = text;
            } 

            if (Text == "-") 
            {
                e.ItemHeight = 8;
                e.ItemWidth  = 4;
                return;
            }

            bool topLevel = Parent == Parent.GetMainMenu();
            string tempShortcutText = shortcuttext;
            if(topLevel) 
            {
                tempShortcutText = "";
            }
            int textwidth = (int)(e.Graphics.MeasureString(Text + tempShortcutText, SystemInformation.MenuFont).Width);
            int extraHeight = 4;
            e.ItemHeight = SystemInformation.MenuHeight + extraHeight;
            if(topLevel)
                e.ItemWidth  = textwidth - 5; 
            else
                e.ItemWidth   = Math.Max(160, textwidth + 50);

            itemHeight = e.ItemHeight;
        }
  
        protected override void OnDrawItem(DrawItemEventArgs e) 
        {
            if(doColorUpdate) 
            {
                DoUpdateMenuColors();
            }

            base.OnDrawItem(e);

            Graphics g = e.Graphics;
            Rectangle bounds = e.Bounds;
            bool selected = (e.State & DrawItemState.Selected) > 0;
            bool toplevel = (Parent == Parent.GetMainMenu());
            bool hasicon  = Icon != null;
            bool enabled = Enabled;

            DrawBackground(g, bounds, e.State, toplevel, hasicon, enabled);
            if (hasicon)
                DrawIcon(g, Icon, bounds, selected, Enabled, Checked);
            else
            {
                if (Checked)
                    DrawCheckmark(g, bounds, selected);
                if (RadioCheck)
                    DrawRadioCheckmark(g, bounds, selected);
            }

            if (Text == "-") 
            { DrawSeparator(g, bounds); } 
            else 
            { DrawMenuText(g, bounds, Text, shortcuttext, Enabled, toplevel, e.State); }
        }

        /* ******************************
        *  DrawRadioCheckmark
        * ******************************/
        private void DrawRadioCheckmark(Graphics g, Rectangle bounds, bool selected) 
        {
            int checkTop = bounds.Top + (itemHeight - BITMAP_SIZE)/2;
            int checkLeft = bounds.Left + ( STRIPE_WIDTH - BITMAP_SIZE)/2;
            ControlPaint.DrawMenuGlyph(g, new Rectangle(checkLeft, checkTop, BITMAP_SIZE, BITMAP_SIZE), MenuGlyph.Bullet);
            g.DrawRectangle(new Pen(framecolor), checkLeft-1, checkTop-1, BITMAP_SIZE+1, BITMAP_SIZE+1);
        }

        private void DrawCheckmark(Graphics g, Rectangle bounds, bool selected) 
        {
            int checkTop = bounds.Top + (itemHeight - BITMAP_SIZE)/2;
            int checkLeft = bounds.Left + ( STRIPE_WIDTH - BITMAP_SIZE)/2;
            ControlPaint.DrawMenuGlyph(g, new Rectangle(checkLeft, checkTop, BITMAP_SIZE, BITMAP_SIZE), MenuGlyph.Checkmark);
            g.DrawRectangle(new Pen(framecolor), checkLeft-1, checkTop-1, BITMAP_SIZE+1, BITMAP_SIZE+1);
        }

        private void DrawIcon(Graphics g, Image icon, Rectangle bounds, bool selected, bool enabled, bool ischecked) 
        {
            int iconTop = bounds.Top + (itemHeight - BITMAP_SIZE)/2;
            int iconLeft = bounds.Left + (STRIPE_WIDTH - BITMAP_SIZE)/2;
            if (enabled) 
            {
                if (selected) 
                {
                    ControlPaint.DrawImageDisabled(g, icon, iconLeft + 1, iconTop, Color.Black);
                    g.DrawImage(icon, iconLeft, iconTop-1);
                } 
                else 
                {
                    g.DrawImage(icon, iconLeft + 1, iconTop);
                }
            } 
            else 
            {
                ControlPaint.DrawImageDisabled(g, icon, iconLeft + 1, iconTop, SystemColors.HighlightText);
            }
        }
 
        private void DrawSeparator(Graphics g, Rectangle bounds)
        {
            int y = bounds.Y + bounds.Height / 2;
            g.DrawLine(new Pen(SystemColors.ControlDark), bounds.X + iconSize + 7, y, bounds.X + bounds.Width - 2, y);
        }
  
        private void DrawBackground(Graphics g, Rectangle bounds, DrawItemState state, bool toplevel, bool hasicon, bool enabled)
        {
            bool selected = (state & DrawItemState.Selected) > 0;

            if (selected || ((state & DrawItemState.HotLight) > 0))
            {
                if (toplevel && selected) 
                {
                    bounds.Inflate(-1, 0);
                    g.FillRectangle(new SolidBrush(barcolor), bounds);
                    ControlPaint.DrawBorder3D(g, bounds.Left, bounds.Top, bounds.Width, bounds.Height, Border3DStyle.Flat, Border3DSide.Top | Border3DSide.Left | Border3DSide.Right);
                } 
                else 
                {
                    if ( enabled ) 
                    {
                    g.FillRectangle(new SolidBrush(selectioncolor), bounds);
                    g.DrawRectangle(new Pen(framecolor), bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
                    }
                    else 
                    {
                    g.FillRectangle(new SolidBrush(barcolor), bounds);
                    bounds.X += STRIPE_WIDTH;
                    bounds.Width -= STRIPE_WIDTH;
                    g.FillRectangle(new SolidBrush(backcolor), bounds);
                    }
                }
            } 
            else
            {
                if (!toplevel) 
                {
                    g.FillRectangle(new SolidBrush(barcolor), bounds);
                    bounds.X += STRIPE_WIDTH;
                    bounds.Width -= STRIPE_WIDTH;
                    g.FillRectangle(new SolidBrush(backcolor), bounds);
                } 
                else 
                {
                    g.FillRectangle(SystemBrushes.Control, bounds);
                }
            }
        }

        private void DrawMenuText(Graphics g, Rectangle bounds, string text, string shortcut, bool enabled, bool toplevel, DrawItemState state ) 
        {
            StringFormat stringformat = new StringFormat();
            stringformat.HotkeyPrefix = ((state & DrawItemState.NoAccelerator) > 0) ? HotkeyPrefix.Hide : HotkeyPrefix.Show;

            if ( toplevel ) 
            {
                int index = text.IndexOf("&");
                if ( index != -1 ) 
                {
                    text = text.Remove(index,1);
                }
            }
   
            int textwidth = (int)(g.MeasureString(text, SystemInformation.MenuFont).Width);
            int x = toplevel ? bounds.Left + (bounds.Width - textwidth) / 2: bounds.Left + iconSize + 10;
            int topGap = 4;
            if (toplevel) topGap = 2;
            int y = bounds.Top + topGap;
            Brush brush = null;

            if (!enabled)
                brush = new SolidBrush(SystemColors.GrayText);
            else
                brush = new SolidBrush(SystemColors.MenuText);

            g.DrawString(text, SystemInformation.MenuFont, brush, x, y, stringformat);

            if ( !toplevel ) 
            {
                stringformat.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
                g.DrawString(shortcut, SystemInformation.MenuFont, brush, bounds.Width - 10 , bounds.Top + topGap, stringformat);
            }
        }

        /* ******************************************************
        *  UpdateMenu(MainMenu yourOldMenu)
        *  UpdateMenu(ContextMenu yourOldMenu)
        * ------------------------------------------------------
        *  this function will update the user old menu
        * ******************************************************/
        /// <summary>
        /// this function will update your Main Menu.
        /// </summary>
        public void UpdateMenu(MainMenu yourOldMenu, NiceMenuClickEvent yourClickFunction)
        {
            IList myMenuList = new Menu.MenuItemCollection(yourOldMenu);
            BuildMenuTree(myMenuList, yourOldMenu, yourClickFunction);
        }
        /// <summary>
        /// this function will update your Context Menu.
        /// </summary>
        public void UpdateMenu(ContextMenu yourOldMenu, NiceMenuClickEvent yourClickFunction)
        {
            IList myMenuList = new Menu.MenuItemCollection(yourOldMenu);
            BuildMenuTree(myMenuList, yourOldMenu, yourClickFunction);
        }
        /* ***********************************************************
        *  BuildMenuTree (IList myMenu, MenuItem parentMenu)
        * -----------------------------------------------------------
        *  build the new submenu item 
        * ***********************************************************/
        private void BuildMenuTree(IList myMenu, MenuItem parentMenu, NiceMenuClickEvent yourClickFunction)
        {
            foreach (MenuItem myMenuItem in myMenu) 
            {
                // Declaration
                NiceMenu newSubMenu;

                string IndexImage = "";
                bool AddMenuImage = false;

                /* If in the first two characters of the menu item text 
                    * there is a number I set AddMenuImage = true and the 
                    * IndexImage to get the icon in the image list control. */
                if (myMenuItem.Text.Length > 2)
                {
                    IndexImage = myMenuItem.Text.Substring(0,2);
                    if (Char.IsNumber(IndexImage,1) == true)
                    {
                        AddMenuImage = true;
                        // I have to delete first two characters
                        myMenuItem.Text = myMenuItem.Text.Substring(2);
                    }
                }
                if (AddMenuImage == true)
                    newSubMenu = new NiceMenu(myMenuItem.Text, new EventHandler(yourClickFunction), myMenuItem.Shortcut, MenuImages.Images[Convert.ToInt32(IndexImage)]);
                else
                    newSubMenu = new NiceMenu(myMenuItem.Text, new EventHandler(yourClickFunction), myMenuItem.Shortcut); 
                // I add the new menu item to its parent
                parentMenu.MenuItems.Add(newSubMenu);
                // Checked 
                if (myMenuItem.Checked == true) newSubMenu.Checked = true;
                // RadioCheck 
                if (myMenuItem.RadioCheck == true) 
                    if (myMenuItem.Checked == true) newSubMenu.RadioCheck = true;
                // DefaultItem 
                if (myMenuItem.DefaultItem == true) newSubMenu.DefaultItem = true;
                // Enabled
                if (myMenuItem.Enabled == false) newSubMenu.Enabled = false;
                // If this menu item contains child menu items
                if (myMenuItem.IsParent == true)
                {
                    IList mySubMenu = new Menu.MenuItemCollection(myMenuItem);
                    BuildMenuTree(mySubMenu, newSubMenu, yourClickFunction);
                }
            }
        }

        /* ***********************************************************
        * BuildMenuTree (IList myMenu, MainMenu parentMenu)
        *  BuildMenuTree (IList myMenu, ContextMenu parentMenu, NiceMenuClickEvent yourClickFunction)
        * -----------------------------------------------------------
        *  build the new main menus and delete the old ones
        * ***********************************************************/
        private void BuildMenuTree(IList myMenu, MainMenu parentMenu, NiceMenuClickEvent yourClickFunction)
        {
            int numOldMenu = myMenu.Count;

            foreach (MenuItem myMenuItem in myMenu) 
            {
                NiceMenu newMainMenu = new NiceMenu(myMenuItem.Text);
                parentMenu.MenuItems.Add(newMainMenu);

                if (myModifyNiceMenu[contModify] == null)
                {
                    myModifyNiceMenu[contModify] = newMainMenu;
                    contModify ++;
                }

                if (myMenuItem.IsParent == true)
                {
                    IList mySubMenu = new Menu.MenuItemCollection(myMenuItem);
                    BuildMenuTree(mySubMenu, newMainMenu, yourClickFunction);
                }
            }
            // Now I have to delete the old menus
            for (int i=0;i<numOldMenu;i++) 
            {   parentMenu.MenuItems.RemoveAt(0);   }
        }

        /* ****************************************************
        * With the context menu I need the yourClickFunction
        * because also the main menu shall be clickable !       
        * ****************************************************/
        private void BuildMenuTree(IList myMenu, ContextMenu parentMenu, NiceMenuClickEvent yourClickFunction)
        {
            int numOldMenu = myMenu.Count;

            foreach (MenuItem myMenuItem in myMenu) 
            {
                // Declaration
                NiceMenu newMainMenu;

                string IndexImage = "";
                bool AddMenuImage = false;

                /* If in the first two characters of the menu item text 
                * there is a number I set AddMenuImage = true and the 
                * IndexImage to get the icon in the image list control. */
                if (myMenuItem.Text.Length > 2)
                {
                    IndexImage = myMenuItem.Text.Substring(0,2);
                    if (Char.IsNumber(IndexImage,1) == true)
                    {
                        AddMenuImage = true;
                        // I have to delete first two characters
                        myMenuItem.Text = myMenuItem.Text.Substring(2);
                    }
                }
                if (AddMenuImage == true)
                    newMainMenu = new NiceMenu(myMenuItem.Text, new EventHandler(yourClickFunction), myMenuItem.Shortcut, MenuImages.Images[Convert.ToInt32(IndexImage)]);
                else
                    newMainMenu = new NiceMenu(myMenuItem.Text, new EventHandler(yourClickFunction), myMenuItem.Shortcut); 

                parentMenu.MenuItems.Add(newMainMenu);

                if (myModifyContextNiceMenu[contModifyContext] == null)
                {
                    myModifyContextNiceMenu[contModifyContext] = newMainMenu;
                    contModifyContext ++;
                }

                if (myMenuItem.IsParent == true)
                {
                    IList mySubMenu = new Menu.MenuItemCollection(myMenuItem);
                    BuildMenuTree(mySubMenu, newMainMenu, yourClickFunction);
                }
            }
            // Now I have to delete the old menus
            for (int i=0;i<numOldMenu;i++) 
            {   parentMenu.MenuItems.RemoveAt(0);   }
        }
    }
}


曾半仙 2003-9-24 20:37:29

这样好像才是比较像吧....

img2

你地那个逊了

MengXP 2003-9-24 21:10:23

呵呵~~~~:)

azmore 2003-9-24 21:32:30

我这儿也有一个,嘻嘻

img3

eym 2003-9-24 22:18:32

xlfancy
问个愚蠢的问题.......C#的正确发音是什么? 是 c sharp 吗?

我怎么听有人 就是发 C# 的音? (我不会拼#的发音)

曾半仙 2003-9-24 22:31:21

微软地呢就是C sharp,不过,有时候必须念C 井

eym 2003-9-25 7:15:01

那在什么情况下念 C 井 呢?

xlfancy 2003-9-25 8:55:19

To:曾半仙、azmore

你们两的的确不错,如果是没调用一个 WIN32 API,纯 C# 搞定的,可不可以给出源码或出处?

另外,我希望看到的是“没用一个 WIN32 API,纯 C# 搞定”

如果是通过调用 WIN32 API 的,哦这还有好几种

曾半仙 2003-9-25 16:56:28

啊????偶的源码是Delphi的,基本上是纯API搞定,没用一个VCL
作界面,用API当然是为了速度,如果改写为纯C#的,我保证在一般性能的机器上都会忽闪忽闪的啊.
忽闪忽闪的意思就象用486跑VB写的skin窗口差不多吧

xlfancy 2003-9-26 9:46:24

可哦这个C#的,在P II上跑的爽歪歪,就是不会忽闪忽闪的

要是在高性能的G上跑...不信你可看看,执行文件如下(VS 2003版) 文件open in new window

Contributors: FHL