创意无极限,用VB7制作您的个性化菜单

类别:.NET开发 点击:0 评论:0 推荐:

创意无极限,用VB7制作您的个性化菜单
   现代的软件开发,由于开发工具的大幅度进步,早已经让编程变得相当简单了,大家使用同样的工具,能不能写出好的程序来,在很大的程度上来说,已经不单单是技术的较量,而更多的是创意的竞争了。如何让用户对您的软件产生好感,我想除了过硬的技术以外,我们还需要一些新奇的东西,让用户有一种耳目一新的感觉!今天我就从软件界面中最常使用的菜单方面着手,教你制作一个个性化的菜单!
VB7中的菜单,如果没有对它编写过特殊的处理程序,虽然不能说难看,但是和现在流行的Office,XP等程序的菜单相比,实在是不够漂亮,如果不想让您的软件看上去象是上个世纪的作品,那么就开始学习编写新的菜单吧!
在VB7中,有两种生成菜单的方法,一种是让系统自动生成,一种是让程序按照一定的指定方式生成。前者可以不用编程,就生成菜单,但是这并不是我们想要的;所以我们选择后者。
好,第一步,我们先在窗体上面放上一个MainMenu控件吧!至于菜单的标题,你喜欢怎么写就怎么写,为了做个示范,我的菜单如下:
文件      MenuItem1
新建      MenuItem2
打开      MenuItem3
保存      MenuItem4
退出      MenuItem5
运行一下这个窗体,我们发现在“文件”的菜单下面出现了四个关于文件的菜单项。好,第一步成功了!但这仅仅只是第一步!接下来,在进行下面的讲解前,我们假设在您的手头上面有一些图标文件,它们是C:\file.ico, C:\new.ico, C:\open.ico, C:\save.ico, C:\exit.ico ,当然如果你手上没有,也可以用其他的图标暂时代替一下,只要能有效果就可以了!好了,既然我们不要程序自动生成菜单,那么我们把这些菜单项的OwnerDraw属性修改一下,在菜单的属性框中找到OwnerDraw,并且设置成True。(这个时候再运行的话,由于还没有写菜单的生成方式,我们将看不到菜单!) 
继续以前,我还要先简单说一下这个菜单的生成原理,菜单的生成靠的时两个过程,一个过程叫作:MeasureItems,另一个过程叫作DrawItem。前者的作用是对菜单的大小进行测量(Measure),后者是在测量的基础上进行绘制(Draw),我们要做的事情就是重新写这两个受保护的事件,让菜单按我们的意愿绘制出来(显示出来)。
添加一个模块,下面的添加这两个事件的具体代码,代码对初学者来说可能有些长,但是通读一遍绝对有很多好处!!我会尽量给出所有的注释。

Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Text
Imports System.Windows.Forms
'以上是这个模块要用到的一些名字空间(只是一些前缀!注意,写了这些,仅仅是为了简化下面的书写,并没有其他任何实际功能。)

Module IconsMenuMain
Sub MeasureItems(ByVal EvMeasureItem  As System.Windows.Forms.MeasureItemEventArgs ,  ByVal Mi As MenuItem,  ByVal MyFont As Font)
        '开始“测量菜单”的事件
        Dim sf As StringFormat = New StringFormat()
        '新建的字符格式对象
        sf.HotkeyPrefix = HotkeyPrefix.Show
        '指定与文本相关的热键前缀的显示类型。将其设置为显示
        sf.SetTabStops(60, New Single() {0})
        '第一个参数firstTabOffset表示
        '文本行开头和第一个制表位之间的空格数。
        '第二个参数tabStops()表示
        '制表位之间的距离(以空格数表示)的数组。
EvMeasureItem.ItemHeight = 22      '设置项目的高度,最好是字体的高度!!
EvMeasureItem.ItemWidth=CInt(EvMeasureItem.Graphics.MeasureString (GetRealText(Mi), MyFont, 10000, sf).Width) + 10      '设置项目的宽度
        sf.Dispose()
        sf = Nothing  '销毁对象
    End Sub

   说明:上面这个事件决定了菜单的大小,也就是菜单的高度和宽度,如果您有兴趣的话,可以将高度常数22,宽度中的附加值常数10 都变成事件参数!这样就有不同高度的菜单了!下面这个事件,我就将窗体菜单的渐变色的起始色和最终色变成了参数,用来分别控制每个菜单中的文字渐变底色!
Sub DrawItems(ByVal EvDrawItems As System.Windows.Forms.DrawItemEventArgs, ByVal Mi As MenuItem, ByVal m_Icon As Icon, ByVal StartColor As Color, ByVal EndColor As Color, ByVal MyFont As Font) 
        Dim br As Brush   '定义刷子
        Dim fDisposeBrush As Boolean   '是否销毁刷子的标志

        If Not m_Icon Is Nothing Then
            '如果没有图标
            If Not Mi.Checked Then
            '无论是否菜单项目被选择了,都先画图标,尽管后者什么都没有EvDrawItems.Graphics.DrawIcon(m_Icon, EvDrawItems.Bounds.Left + 2, EvDrawItems.Bounds.Top + 2)
            Else
                EvDrawItems.Graphics.DrawIcon(m_Icon, EvDrawItems.Bounds.Left + 2, EvDrawItems.Bounds.Top + 2)

                Dim nPen As System.Drawing.Pen
                If Not Mi.Enabled Then
                   nPen = New Pen(Color.DarkGray) '如果可用,就填充颜色DarkGray
                Else
                    nPen = New Pen(Color.Gray)     '不然用颜色Gray
                End If
        EvDrawItems.Graphics.DrawRectangle(nPen,1,EvDrawItems.Bounds.Top, 20, 20)
 EvDrawItems.Graphics.DrawRectangle(nPen, 3,  EvDrawItems.Bounds.Top + 2, 16, 16)
                '画上两个框,两个框构成一个“回“字型
            End If
        Else
            '如果有图标
            If Mi.Checked Then   '如果菜单处于复选状态  
                Dim nPen As System.Drawing.Pen  新建一个画笔对象
                If Not Mi.Enabled Then   根据是否可用决定画笔颜色
                    nPen = New Pen(Color.DarkGray)
                Else
                    nPen = New Pen(Color.Gray)
                End If
                EvDrawItems.Graphics.DrawRectangle(nPen, 1,EvDrawItems.Bounds.Top, 20, 20)
                '有图标,同时被选中,则只要画“回“字型中较大的一个框 
                Dim Pnts() As Point
                ReDim Pnts(2)    '这三个点就是一个“勾“的位置
                Pnts(0) = New Point(15, EvDrawItems.Bounds.Top + 6)
                Pnts(1) = New Point(8, EvDrawItems.Bounds.Top + 13)
                Pnts(2) = New Point(5, EvDrawItems.Bounds.Top + 10)
                '根据是否可用来决定画“勾“的颜色,并且画上“勾”
                当然你可以根据需要来创造您自己菜单复选的图形
                If Mi.Enabled Then
                    EvDrawItems.Graphics.DrawLines(New Pen(Color.Black), Pnts)
                Else
                    EvDrawItems.Graphics.DrawLines(New Pen(Color.Gray), Pnts)
                End If
            End If
        End If


        Dim rcBk As Rectangle = EvDrawItems.Bounds
        rcBk.X += 24   '先获得一个和菜单一样大小的矩形,然后将矩形的X方向长度缩短,缩短的大小就是图标的大小,以后将以这个矩形作为菜单文字背景底色的填充范围

        '根据菜单项目是否被选中来决定是否使用颜色渐变刷子,
        '如果是则设置刷子的颜色,同时将是否销毁刷子的标志设置为真
        If CBool(EvDrawItems.State And DrawItemState.Selected) Then
            br = New LinearGradientBrush(rcBk, StartColor, EndColor, 0)
            设置起始色和最终色
            fDisposeBrush = True  ‘设置刷子销毁标志
        Else
            br = SystemBrushes.Control   ‘这个颜色是系统的默认
        End If
        '画上渐变的菜单文字底色
        EvDrawItems.Graphics.FillRectangle(br, rcBk)

        If fDisposeBrush Then br.Dispose() '前面使用New方法新建的渐变颜色刷子的实例要销毁,但是前面的声明还是有效的

        br = Nothing    '将整个刷子暂时清空

        Dim sf As StringFormat = New StringFormat()
        sf.HotkeyPrefix = HotkeyPrefix.Show
        '指定与文本相关的热键前缀的显示类型。将其设置为显示
        sf.SetTabStops(60, New Single() {0})
        '第一个参数firstTabOffset表示
        '文本行开头和第一个制表位之间的空格数。
        '第二个参数tabStops()表示
        '制表位之间的距离(以空格数表示)的数组。
        If Mi.Enabled Then '根据菜单是否可用来决定图像刷子的颜色
            br = New SolidBrush(EvDrawItems.ForeColor)
        Else
            br = New SolidBrush(Color.Gray)
        End If
        '在菜单上面写字
        EvDrawItems.Graphics.DrawString(GetRealText(Mi),MyFont,br,EvDrawItems.Bounds.Left + 25, EvDrawItems.Bounds.Top + 2, sf)
        br.Dispose()
        br = Nothing
        sf.Dispose()
        sf = Nothing
    End Sub

    Function GetRealText(ByVal Mi As MenuItem) As String
这个事件是用来获得菜单文本的
        Dim s As String = Mi.Text  '获得菜单原始文本
        If Mi.ShowShortcut And Mi.Shortcut <> Shortcut.None Then
            Dim k As Keys = CType(Mi.Shortcut, Keys)
            '如果有菜单快捷键就让菜单文本加上快捷键文本
s = s & Convert.ToChar(9) & TypeDescriptor.GetConverter(GetType(Keys)).ConvertToString(k)
        End If
        Return s
    End Function
    说明:当然您可以让菜单的文本变成任何您想要的文本!
End Module

好了,最后一步就要看看我们的实际效果了,在原来的窗体上写上代码吧!!
下面我们以“美化”第一,第二个菜单项为例子,当然,一般主菜单不应该有图标的。看懂了以后,各位按照自己的想象力来画自己的菜单好了。

    Private Sub MenuItem1_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) Handles MenuItem1.MeasureItem
第一个测量事件 ,特别注意“Handles“标志 !
        Dim MyFont As New System.Drawing.Font("Arial", 8) ‘定义一个你喜欢的字体
        MeasureItems(e, MenuItem1, Myfont)
     End Sub

    Private Sub MenuItem1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles MenuItem1.DrawItem
        Dim Ic As New Icon("C:\file.ico")   ‘定义一个您喜欢的图标
        Dim MyFont As New System.Drawing.Font("Arial", 8)  ‘这个字体最好和前面保持一致,当然如果您愿意,也可以在前面一种字体的确定范围用新字体写字,但是字体最好不要设置得太过火了,不然可能有意想不到的后果!
        DrawItems(e, MenuItem2, Ic, Color.Blue, Color.Yellow, Myfont)
     End Sub

Private Sub MenuItem2_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles MenuItem2.DrawItem
        e.Graphics.CompositingQuality=Drawing.Drawing2D.CompositingQuality.HighQuality
        Dim Ic As New Icon("C:\open.ico")
        Dim MyFont As New System.Drawing.Font("Arial", 14)
        DrawItems(e, MenuItem2, Ic, Color.Tomato, Color.LavenderBlush, MyFont)
    End Sub

    Private Sub MenuItem2_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) Handles MenuItem2.MeasureItem
        Dim MyFont As New System.Drawing.Font("Arial", 14)
        MeasureItems(e, MenuItem2, MyFont)
    End Sub
运行一下,你会得到一个主菜单也有图标,各个菜单项的字体和底色都不同的菜单!这只是一个演示,但是最好不要在真正的软件中这样设计,菜单应该保持统一的风格。
这篇文章只是介绍一个修改菜单的基本方法,我希望大家能够举一反三,发挥自己的创造力,制作出自己的个性化菜单来!

本文地址:http://com.8s8s.com/it/it45691.htm