Henry手记-.NET WinForm菜单DIY

** Henry ** ** 手记- ** ** .NET WinForm ** ** 菜单 ** ** DIY **

韩睿( 2003.7.29 )

天气很热,上海也达到了六十年来的最高温,为了防止出门中暑,只好闭关自修。趋着心情不错,写一点小东西以馈读者,希望大家在酷暑中获得一份轻松与惬意。

今天和大家讨论一下对菜单的改造。网上有不少好控件,都能实现在 Menu 里加入图片,但是少有更改 Menu 中的字体与颜色的。其实,我们通过对 .net 自带控件的操作,就可以实现加图片与改字体与颜色的目标。本文主要是讨论字体与颜色的更改。如下图所示:

通过菜单项的改变,窗体上的“天气真热” Label 会相应地改变字体与前景色。

有的朋友会问,一般的控件不是只要设置 font 、 forecolor 、 backcolor 属性不就能达到 Henry 要做的事情了吗?但是 Menu 不行, 原因是 : Menu 的父类是 Component ,不是 Control! 而那些属性是 Control 类的,如果 Menu 是从 Control 类继承下来的,当然就不会有我这篇小文的存在意义了。所以我们应该把 Menu 称为组件,以示与 Control 的派生控件相区别。大家以后使用控件时,也要注意一下它的父类是什么,以明确是否能进行某项功能的实现。

Menu 内含三个类: MainMenu (主菜单)、 ContextMenu (弹出菜单)、 MenuItem (菜单项)其中, MainMenu 和 ContextMenu 都是容器, ** MenuItem ** 类提供使我们得以配置菜单项的外观和功能的属性。因此,我们的工作主要就是针对 MenuItem 去的。上图实现的是在 ContextMenu 菜单, MainMenu 的形式也是一样的。

现在我们是要做打破常规的事,所以要将 MenuItem 的 OwnerDraw 设为 true ,接管下绘制菜单项的重任,自已动手 DIY 一把。

那么我们来分析一下,当 OwnerDraw 设为 true 之后,哪些事件或方法是需要我们重写的。查一下 MSDN 就可以看到: DrawItem 事件是当 OwnerDraw 设为 true 后发生的,接着查一下它的帮助,看一下事件处理程序中的事件数据是如何定义的。

[MSDN]: 事件处理程序接收一个 DrawItemEventArgs 类型的参数,它包含与此事件相关的数据。下列 DrawItemEventArgs 属性提供特定于此事件的信息。

** 属性 ** ** **

|

** 说明 ** ** **

|

** 属性 ** ** **

|

** 说明 ** ** **

---|---|---|---

BackColor

|

获取所绘制的项的背景色

|

Graphics

|

获取要在其上绘制项的图形表面

Bounds

|

获取表示所绘制项的边界的矩形

|

Index

|

获取所绘制项的索引值

Font

|

获取分配给所绘制项的字体

|

State

|

获取所绘制项的状态

ForeColor

|

获取所绘制项的前景色

|

|

果然,我们需要的三个属性全都在这了!那么,我们实现这一事件不就行了?我按 MSDN 的例子实现了它,可是,出来的效果却不对, ContextMenu 的宽度与高度都只有一点点大。看来是没有设置好 MenuItem 的宽与高了。

Item 的宽与高在哪设置? MenuItem 的属性中并没有这两项,找来找去,最后在 MeasureItem 事件中找到了。

** 属性 ** ** **

|

** 说明 ** ** **

---|---

Graphics

|

获取要测量的 Graphics 对象

Index

|

获取或设置需要有高度和宽度的项索引

ItemHeight

|

获取或设置由 Index 指定的项高度

ItemWidth

|

获取或设置由 Index 指定的项宽度

也就是说,我们想要绘制出 MenuItem ,必须先计算出将要绘制的 Item 的宽度与高度。在这里需要说明的是,项的宽度与高度不光是由文字部分决定的,还取决于 item 中的图片部分,所以我们的计算公式是:

项的宽度=图片的宽度 + 文字的宽度

项的高度 =max ( 图片的高度,文字的高度 )

本文没有加入自定义图片,只是加入了一个圆点的选项符号,它是由 DrawMenuGlyph 共享方法实现的。那么它的高度与宽度是如何界定的呢?很简单,使用 SystemInformation.MenuCheckSize 就可确定在选定菜单项旁显示选中标记的图像的尺寸。圆点的默认大小是高宽皆为 13 像素。 DrawMenuGlyph 是会根据我们给出的图片的位置大小绘制圆点的大小的。所以,我们还必须在 DrawItem 事件中给出图片的大小:

图片的高度 = 文字的高度

图片的宽度=图片的默认宽度 * 放大的倍数=图片的默认宽度 * 文字的高度 \ 图片的默认高度

昏头昏脑地说了这么多,还是用代码来描述更为清晰:

Dim oldMenuItem As New MenuItem()

Private Sub Form1_Load( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase .Load

Me .ContextMenu = New ContextMenu()

Dim MenuText As New ArrayList()

MenuText.Add("宋体")

MenuText.Add("黑体")

MenuText.Add("楷体_GB2312")

Dim MyMenuItem(MenuText.Count - 1) As MenuItem

Dim i As Integer

For i = 0 To MyMenuItem.Length - 1

MyMenuItem(i) = New MenuItem(MenuText(i))

MyMenuItem(i).OwnerDraw = True '每一项都要设

MyMenuItem(i).RadioCheck = True '每一项的选项钮都开启

'指向单击事件的处理程序

AddHandler MyMenuItem(i).Click, _

New EventHandler( AddressOf MenuItem_Click)

'指向MeasureItem事件的处理程序

AddHandler MyMenuItem(i).MeasureItem, _

New MeasureItemEventHandler( AddressOf MenuItem_MeasureItem)

'指向DrawItem事件的处理程序

AddHandler MyMenuItem(i).DrawItem, _

New DrawItemEventHandler( AddressOf MenuItem_DrawItem)

ContextMenu.MenuItems.Add(MyMenuItem(i))

Next i

'记录下初始选中的项

oldMenuItem = ContextMenu.MenuItems(0)

oldMenuItem.Checked = True

End Sub

Private Sub MenuItem_Click( ByVal obj As Object , ByVal e As EventArgs)

oldMenuItem.Checked = False '改变原有的菜单项的选取状态

oldMenuItem = CType (obj, MenuItem) '转型处理

oldMenuItem.Checked = True '设置现选中的菜单项的选取状态

Select Case oldMenuItem.Text '根据菜单内容设置label的色彩

Case "宋体"

<SPAN lang=EN-US style="FONT-SIZE: 9pt; BACKGROUND: #d9d9d9; FONT-FAMILY: 新宋体; mso-bidi-font-size: 11.0pt; mso-hansi-font-family: 'Times New Roman'; mso-font-kerning: 0pt; mso-shading: white; mso-pattern: gray-15 auto

Published At
Categories with Web编程
Tagged with
comments powered by Disqus