简介
仅仅使用一行简单的程序,你就能够使你的Windows窗体的所有菜单和上下文菜单具有office2003的菜单外观。同样地,你也可以只用一行程序,就能为你的菜单加上漂亮的图标。本文实现的是一个具有该功能的组件。如果你想让你的菜单恢复原来的外观,也只须调用End方法即可。

组件的使用
要正确使用组件,必须先将你的组件加入到工具箱中。然后将该组件从工具箱中拖放放到form窗体中。这时会看到你的form的设计页中多出了一个名为OfficeMenus1的图标,说明已经将菜单组件加入到了form中。紧接着调用如下方法:

//开始显示office 2003菜单
OfficeMenus1.Start( FormName ); 注:FormName为要改变菜单风格的窗口名称。

同样,你也可以通过调用如下方法终止菜单的office2003风格,使之回到原始外观:

// 改变菜单的外观风格到原始状态
OfficeMenus1.End();

为菜单顶添加图标也很简单,只须为工程添加一个ImageList(图像列表控件),然后将OfficeMenu组件的ImageList属性更改为你添加的ImageList,使用如下代码实现:

// 为菜单添加图像
// OfficeMenus.AddPicture( MenuItem MenuItemToAddPictureTo, int ImageIndex );
OfficeMenus1.ImageList = imageList1;
OfficeMenus1.AddPicture(menuItem2, 1);

可以看出,只须如此几行代码就能轻松让你的菜单实现office2003风格。

组件的实现方法及原理
组件由三个类实现,这三个类分别为OfficeMenus,MainMenuItemDrawing和MenuItemDrawing,都位于命名空间Dev4Arabs中。由于实现代码较长,所以在此只给出了组件实现的思路。
组件实现的第一步是从System.ComponentModel.Component类派生类OfficeMenus。定义如下:

public class OfficeMenus : System.ComponentModel.Component

然后在类中定义两静态变量:

//图像列表用来存储菜单中用到的图标

static ImageList _imageList;

// 存储图片细节的一个名称集合,NameValueCollection的详细说明请查阅MSDN,该类主要用来使每个菜单的句柄与每个图标形成一一对应的关系,以便后面绘制菜单顶的图标时快速地找到某个菜单所对应的图标。

static NameValueCollection picDetails = new NameValueCollection();

接下来定义公共接口方法start和End。
公共接口方法Start的实现原理如下:

public void Start(Form form)
{  先从Start传入的参数中获得该窗口的主菜单。接下来为每一个主菜单下的MenuItem添加MeasureItem事件处理mainMenuItem_MeasureItem和DrawItem事件处理mainMenuItem_DrawItem,将MenuItem的OwnerDraw属性设置为true,并使用InitMenuItem(Menu mi)方法对每个MenuItem应用改变。然后再从form参数中获取该窗口的上下文菜单对象,对其调用InitMenuItem(Menu mi)应用改变。最后循环查找窗口中包含的每个子控件的上下文菜单,对其应用改变。System.Windows.Forms.MainMenu menu = form.Menu;foreach ( MenuItem mi in menu.MenuItems ){mi.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(mainMenuItem_MeasureItem);mi.DrawItem += new System.Windows.Forms.DrawItemEventHandler(mainMenuItem_DrawItem);mi.OwnerDraw = true;InitMenuItem(mi);}ContextMenu cmenu = form.ContextMenu;if ( cmenu != null ) {InitMenuItem(cmenu);}foreach ( Control c in form.Controls ) {if ( c.ContextMenu != null )InitMenuItem(c.ContextMenu);}catch {}
}

End方法与这类似,只须将InitMenuItem换为UninitMenuItem,这里就不再举出了。具体实现代码请到http://www.codeproject.com/cs/menu/MhOffice2003Menus.asp下载。
为每个菜单应用改变的InitMenuItem方法的实现如下:

private void InitMenuItem(Menu mi)
{
循环查找mi中的每个MenuItem,为其添加MeasureItem事件处理menuItem_MeasureItem和
DrawItem事件处理menuItem_DrawItem(注意,此处的事件处理方法名称与Start中对
主菜单的事件处理方法名称不同),并将OwnerDraw属性设置为true。使用递归调用对每个查找
到的MenuItem调用InitMenuItem方法,这样便可以对菜单项下的所有级别的子菜单项都应用到改变。
}

UninitMenuItem的定义与此类似。
menuItem_MeasureItem事件处理方法定义如下:

private void menuItem_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
{先取得要进行消息处理的MenuItem对象(MenuItem item = (MenuItem) sender;)。          if (item为seperator ) {  e.ItemHeight = 7;} else {获取item的文字宽度,如果有快捷键,还要获取item中的快捷键所占用的宽度。设置item的边界:e.ItemHeight = 文字高度+7; e.ItemWidth = 文字宽度 + 快捷键的宽度 + 图标宽度*2;}
}

menuItem_DrawItem的实现如下:

private void menuItem_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{MenuItemDrawing.DrawMenuItem(e, (MenuItem) sender);
}

该函数只是简单地调用了类MenuItemDrawing中的静态方法DrawMenuItem。
Start中为主菜单的子菜单添加事件处理的mainMenuItem_MeasureItem和mainMenuItem_DrawItem的定义如下:

private void mainMenuItem_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
{MenuItem mi = (MenuItem) sender;//获得菜单项对象SizeF miSize = e.Graphics.MeasureString(mi.Text, Globals.menuFont);//由于顶级菜单(如文件菜单)无快捷键和图标,所以绘制的宽度为文字的宽度。e.ItemWidth = Convert.ToInt32(miSize.Width);
}private void mainMenuItem_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{MainMenuItemDrawing.DrawMenuItem(e, (MenuItem) sender);
}

该方法只是调用了类MainMenuItemDrawing中的静态方法DrawMenuItem进行菜单绘制。
其它方法如AddPicture和GetItemPicture的定义如下:

public void AddPicture(MenuItem mi, int index)
{
//将菜单项的句柄转化为字符串与图标的索引一一对应添加到picDetails集合中。picDetails.Add(mi.Handle.ToString(), index.ToString());
}
public static Bitmap GetItemPicture(MenuItem mi)
{if ( _imageList == null )return null;//将菜单项的句柄作为键查找该键对应的值,返回值为图标索引string [] picIndex = picDetails.GetValues(mi.Handle.ToString());if ( picIndex == null )return null;else//根据索引取出位图对象并返回return (Bitmap)_imageList.Images[Convert.ToInt32(picIndex[0])];
}

类MainMenuItemDrawing主要负责主菜单的一级子菜单(如常见的文件菜单)的绘制。
实现如下:

public class MainMenuItemDrawing
{//静态方法,实现菜单项的绘制 public static void DrawMenuItem(System.Windows.Forms.DrawItemEventArgs e, MenuItem mi){首先检查menuItem的状态,如为鼠标悬浮在其上的状态,则调用DrawHoverRect绘制并填充悬浮矩形;如为选定态,则调用DrawSelectionRect绘制并填充相应的选定态时的矩形;如两者都不是,则用控件的填充色绘制并填充矩形。最后利用e.Graphics.DrawString方法绘制菜单文字。}……
}

类MenuItemDrawing负责主菜单的子菜单和上下文菜单的绘制。

public class MenuItemDrawing
{//静态方法,实现菜单项的绘制public static void DrawMenuItem(System.Windows.Forms.DrawItemEventArgs e, MenuItem mi){检查菜单顶是否被选中,如被选中,则调用DrawSelectionRect绘制并填充选中后的矩形,否则只用背景色绘制空白区域并调用DrawPictureArea绘制图片区域。调用DrawCheckBox绘制复合框如果该菜单项被选中。调用DrawMenuText绘制菜单项文字。最后调用DrawItemPicture绘制图标。}……
}

你对源码有兴趣。可以下载本文提供的源代码也可以从http://www.codeproject.com/cs/menu/MhOffice2003Menus.asp下载。

由于绘制的代码比较长,不宜在文中全部给出,所以具体的绘制代码将省去。文中只是给出了实现该组件的思路。如果结束语:
由于.net中使用了GDI+,所以组件的绘制工作比以前在MFC或者Win32API模式下绘制要容易地多。组件的开发最重要的一点就是当菜单项被置为自绘方式后,用户需要激活两个事件来定制菜单的显示。第一个事件对应Win32的WM_MEASUREITEM消息。窗口收到这个消息时,它就会触发一个 MeasureItem 事件给所有的自绘 MenuItem 对象。这个事件代理(Delegate)是一个名为MeasureItemEventHandler的类,与此事件相关的信息都被存储在一个MeasureItemEventArgs 对象中并被传递到事件处理函数(文中为mainMenuItem_MeasureItem或者menuItem_MeasureItem)。第二个事件与 Win32 的WM_DRAWITEM消息对应,并给每个注册了的事件处理函数传递一个 DrawItemEventArgs 对象。这个事件代理是一个名为DrawItemEventHandler的类。个人认为组件实现的难点和重点就是在两事件处理函数中根据菜单的不同状态所要进行的不同绘制工作。

转载于:https://www.cnblogs.com/hzuIT/articles/472792.html

Windows窗体的所有菜单相关推荐

  1. 利用C++Builder自定义Windows窗体“系统菜单”

    利用C++Builder自定义Windows窗体"系统菜单" 安徽合肥智能机械研究所张建军 -------------------------------------------- ...

  2. C#创建Windows窗体应用程序实例9【菜单设计】

      都实例9了?你还不会创建项目?不会操作步骤?不会写代码?请进入下方链接学习吧! C#创建Windows窗体应用程序实例1https://blog.csdn.net/qq_45037155/arti ...

  3. VS2010 win32项目windows窗体程序 向导生成代码解析

    目录: 1.Win32项目的windows窗体程序的向导生成了如下代码 2.手工生成代码如下 3.当消息队列中没有消息需要处理,我们可以利用这段时间处理我们自己的任务 1.Win32项目的window ...

  4. 创建可按比例调整的布局的 Windows 窗体

    能够正确调整大小的窗体可以提高您的用户界面的易用性. 此演练演示了如何创建当用户调整窗体大小时按比例调整的布局. 您将使用 TableLayoutPanel 控件实现一个接收联系人信息的数据输入窗体. ...

  5. 演练:在 Windows 窗体中承载 Windows Presentation Foundation 复合控件 【转载】

    http://msdn.microsoft.com/zh-cn/library/ms745781.aspx 更新:2007 年 11 月 本演练演示如何创建 WPF 复合控件,并通过使用 Elemen ...

  6. 移动设备应用程序开发入门一:创建用于设备的 Windows 窗体应用程序并打包进行部署...

    目标:了解如何使用 Visual C# 创建一个简单的 Hello World 应用程序,该应用程序可在 Pocket PC 上运行. 了解如何为应用程序创建随后可部署到智能设备上的 .cab 文件. ...

  7. 演练:调试 Windows 窗体

    Windows 窗体是最常见的托管应用程序之一. Windows 窗体创建标准的 Windows 应用程序. 你可以完成此演练使用 Visual Basic 中, C#,或 c + +. 首先,您必须 ...

  8. Windows窗体和常用控件的事件处理函数

    Windows窗体  WM_NULL = $0000;  WM_CREATE = $0001; 应用程序创建一个窗口  WM_DESTROY = $0002; 一个窗口被销毁  WM_MOVE = $ ...

  9. 袖珍计算器c语言设计源码,Calculator 示例:Windows 窗体袖珍计算器

    Calculator 示例:Windows 窗体袖珍计算器 08/12/2011 本文内容 Calculator 示例使用 C++ 和 .NET Framework Windows 窗体类实现一个简单 ...

最新文章

  1. WMI技术介绍和应用——查询环境变量
  2. Github上十大热门可视化面板!再也不用担心画图啦!
  3. Oracle APEX 系列文章2:在阿里云上打造属于你自己的APEX完整开发环境 (准备工作)...
  4. aspx练习备忘录#想锤自己两拳#1
  5. java 获取六个月账期,应收帐龄分析里面账期分析能不能改为0-30天?
  6. 4怎么修边_亦木良品阻燃板怎么样
  7. centos 静态拨号
  8. 在awk中执行外部命令
  9. 3gpp协议_春天工作室lt;3GPP规范翻译系列gt;1:TS37340协议翻译(导读)第一部分...
  10. r语言mfrow全程_如何使用R完成文章中图片处理小教程
  11. 蓝桥杯 基础练习 报时助手
  12. Win2003安装不了 Windows Media Player 11
  13. 74ls20设计半加器_实验二++组合逻辑电路的设计与测试.ppt
  14. MATLAB图形绘制--极坐标和对数图像
  15. 8年码龄的技术总监,去上市公司面试,结果凉了!
  16. java file exists用法_Java File exists()方法
  17. 阿里又上美国“售假黑名单” 是被故意抹黑了吗
  18. 关于mavon-editor中iframe 的使用 和插入视频、音频的记录
  19. VMwear虚拟机安装动态网卡和静态网卡(Basic Service版)
  20. Unity3D实践系列06,球体撞击物体游戏

热门文章

  1. 《Adobe Illustrator大师班:经典作品与完美技巧赏析》目录—导读
  2. UVa10375 Choose and divide
  3. ubuntu 12.04 添加 IP并配置DNS
  4. 【ASP.NET 问题】Win7中IIS被防火墙阻止导致外网无法访问的解决方法。
  5. 宽带服务价值链之:ISP,ICP,ASP,IDC,CDN
  6. 毒霸能清除的大小流氓清单(部分)
  7. 细思极恐的2016智能交通!怎么回事?
  8. 处理异步利器 -- Redux-saga
  9. CSS 实现打字效果
  10. C#复制图片_并重命名