本类文章的标签为 ‘表驱动’


细品RibbonX(46):在Excel 2007的QAT中以表驱动的方式构建自定义菜单

1 颗星2 颗星3 颗星4 颗星5 颗星 (目前还没有人投票)
Loading ... Loading ...

一、创建在所有工作簿中都能使用的自定义菜单
如果想在所有工作簿中都可以使用自已喜欢的宏,那么可以将这些宏复制到Personal.xlsb工作簿中,或者在XLStart文件夹中使用另一个隐藏的xlsb工作簿(在Excel启动时会打开该文件夹中的每一个文件),也可以创建加载项。
XLSTART文件夹的位置

C:\Documents and Settings\(username)\Application Data\Microsoft\Excel\XLSTART

如果找不到指定的文件或文件夹,则可能是Windows设置将其隐藏了,此时需要在文件夹选项中启动“显示所有文件和文件夹”选项。
如何在Excel2007中创建菜单
在Excel 97-2003中,在已存在的菜单栏中创建一个新菜单或者创建自定义菜单栏一点也不困难。但是在Excel 2007中,定制功能区并不容易。
1) 在下面的地址中下载文件MyMacroFile.zip:

http://www.rondebruin.nl/files/My%20Add-in.zip

2) 解压并复制该文件到XLSTART文件夹中,然后打开Excel(不能看到该文件,因为它是隐藏的)。
3) 在快速访问工具栏(QAT)中单击鼠标右键,选择“自定义快速访问工具栏”。
在“从下列位置选择命令”下拉框中选择“宏”,然后在“自定义快速访问工具栏”下拉框中选择“用于所有文档(默认)”。
选择“DisplayPopUp”宏,按下“添加” ,然后单击“确定”按扭,如图1所示。
customqatsample1
图1
在图1中,可以使用“修改”按钮命令来改变图标。
注 : 仅需执行操作一次,因为该按钮被保存在Excel QAT定制文件中。
如果不想再使用该菜单,则从XLSTART文件夹中移除该xlsb文件后,还需手工从QAT中删除该菜单按钮。
4) 如果在QAT中单击该图标,则将弹出自定义的菜单,如图2所示。
customqatsample2
图2
编辑该菜单:
在功能区“视图”选项卡中单击“取消隐藏”命令,在弹出的对话框中选择MyMacroFile.xlsb 文件并单击“确定”按钮。
此时,将显示如图3所示隐藏的工作表“MenuSheet”:
customqatsample3
图3
Level: 指定菜单项的层级,有效值为2和3。2级代表菜单项,3级代表子菜单项。
Caption: 显现在菜单、菜单项或子菜单里的文本,使用符号(&)来指定加下划线(热键)的字符。
Macro name: 对于2级或3级项目,在选择该项时要执行的宏。如果2级项目有一个或多个3级项,则2级项目可能没有与之相关联的宏。使用Alt+F11键打开VBE编辑器,可以在MacroModule模块中添加或修改宏程序。
Divider: 值为True时,则在菜单项或子菜单项前放置一个分隔条。
FaceID: 可选的。代表显示在项目旁边的内置图形图像的代号数字。
您可以编辑该表中的信息,从而创建自已的菜单。单击“Refresh Menu”按钮来查看是否作出了正确的修改。如果正确,则单击“Hide Save”按钮。
二、创建只在一个工作簿中可用的自定义菜单
本节的内容与上节内容大致相同,主要的区别在于工作簿文件为xlsm工作簿,直接打开该工作簿,并不需要将其放置在特定的文件夹中。
在下面的地址中下载MyWorkbook.xlsm工作簿文件。

http://www.rondebruin.nl/files/MyWorkbook.zip

下载该工作簿后,直接在Excel中打开该工作簿,然后按照上节3)以后的内容进行操作即可。
三、在加载项中存储自定义菜单
可以将带有自定义菜单的工作簿保存为Excel加载项(xlam),然后再在工作簿中启用该加载项。这样,QAT中的按钮将保存在加载项中,并且可以在所有打开的工作簿中使用。
对于上面介绍的示例工作簿,只需将下面的过程中的两行代码删除或注释掉,然后将其保存为Excel加载项。

Sub WBDisplayPopUp()
' If ActiveWorkbook.Name = ThisWorkbook.Name Then
    On Error Resume Next
    Application.CommandBars(ThisWorkbook.Sheets("MenuSheet").Range("B2").Value).ShowPopup
    On Error GoTo 0
' End If
End Sub

创建带有菜单的加载项,而这些菜单中是您想要分发的宏程序。这是一种很好的方式。

注:本文参考了Ron de Bruin的一系列文章,有兴趣的朋友可以直接参考其网站的文章。
同时,参见:表驱动的方式构建自定义菜单

相关文章

细品RibbonX(45):在快速访问工具栏(QAT)中添加项目

1 颗星2 颗星3 颗星4 颗星5 颗星 (目前还没有人投票)
Loading ... Loading ...

快速访问工具栏(QAT)是Office 2007新用户界面的一部分,可以使用老的Office工具栏定制方法进行定制。下面,我们主要介绍如何使用XML定制QAT。
概述
QAT可以包含共享控件和特定文档控件,也可以包含整个控件组(内置的和自定义的)以方便地在单一位置存储多个控件。
要定制QAT,必须从头开始设计用户界面,这意味着必须将startFromScratch属性设置为true:

   <ribbon startFromScratch="true">

在处理QAT时,你会注意到有两类图标,一种在其周围有边框而另一种则没有,这种区别表明哪种控件是共享控件,哪种控件是文档控件。
QAT文档控件的XML代码如下:

   <qat>
     <documentControls>
        <control/>
     </documentControls>
   </qat>

QAT共享控件的XML代码如下:

    <qat>
        <sharedControls>
            <control/>
        </sharedControls>
    </qat>

在QAT中添加自定义和内置命令
快速访问工具栏共享或文档控件的子元素如下表。
表:QAT的子元素

对象 用来做什么
control 引用可以表现其它对象例如按钮、拆分按钮、组等的普通控件对象
button 引用按钮控件
separator 引用分隔条控件


如下图所示,在QAT中添加内置控件和自定义按钮。
QAT1
XML代码如下:

   <qat>
     <documentControls>
	<control 
       idMso="Bold"
       screentip="Make it Bold"
       supertip="Click here to make the selected text bold."/>
      <button
       id="rxbtnOpen"
       imageMso="FileOpen"
       screentip="This is Happy"
       supertip="Click here for a happy message"
       onAction="rxbtnOpen_click"/>
     </documentControls>
    </qat>

可以使用control对象引用内置的按钮(本例中为加粗按钮),接着使用按钮创建自已的定制按钮(可以使用control对象引用其它控件例如按钮或拆分按钮)。
下面的示例创建一个splitButton控件,然后将其添加到QAT。因为QAT没有splitButton子元素,我们必须在QAT之外创建splitButton,然后将其引用到QAT。可以通过以普通的方式添加splitButton来实现——也就是说,通过将其添加到组中开始:

       <group 
        id="rxgrp" 
        label="My Custom Group">
        <splitButton 
         id="rxsbtn" 
         size="large">
         <button 
          id="rxbtn2" 
          imageMso="HappyFace" 
          label="My Happy Split"/>
          <menu 
           id="rxmnu">
           <button 
            id="rxbtn3" 
            label="My Happy Menu"
            imageMso="HappyFace"
            onAction="rxbtn3_click"/>
          </menu>
        </splitButton>
        </group>

上面已经有splitButton并带有一个菜单,菜单中包含一个按钮。由于QAT使用现有的控件,因此现在可以在QAT中引用已存在的拆分按钮了,XML代码如下:

   <qat>
     <documentControls>
      <control
       id="rxsbtn"
       imageMso="HappyFace"
       screentip="This is Happy"
       supertip="Click here for a happy message"/>
     </documentControls>
    </qat>

结果如下图所示。
QAT2
在QAT中添加自定义和内置组
必须首先创建组,然后从QAT中引用指定该组的id。可以添加组到选项卡中并使其在选项卡中不可见而在QAT中可见。
QAT3
如上图所示,My QAT Custom Group属于自定义的“Home”选项卡,然而我们将其可见属性设置为False,使其在选项卡上隐藏而在QAT中可见。XML代码如下:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
   <ribbon startFromScratch="true">
     <tabs>
      <tab
       id="rxtabHome"
       label="Home">
       <group
        idMso="GroupClipboard"/>
       <group
        idMso="GroupFont"/>
       <group
        idMso="GroupAlignmentExcel"/>
       <group
        idMso="GroupNumber"/>
       <group
        idMso="GroupStyles"/>
       <group
        idMso="GroupCells"/>
       <group
        idMso="GroupEditingExcel"/>
	 <group
	  id="rxgrp" 
	  label="My QAT Custom Group"
        getVisible="rxshared_getVisible">
 
	  <button 
	   id="rxbtnHappy" 
	   label="Mr. Happy Face" 
	   imageMso="HappyFace" 
	   size="large" 
	   onAction="rxshared_click" />
	  <button 
	   id="rxbtnHappy2" 
	   label="Mr. Happy Face 2" 
	   imageMso="HappyFace" 
	   size="large"
	   onAction="rxshared_click" />
	 </group>
 
      </tab>
     </tabs>
 
    <qat>
     <documentControls>
	<control
	  idMso="GroupInsertChartsExcel"/>
      <control
        idMso="GroupFunctionLibrary"/>
      <control
        id="rxgrp"
        imageMso="FormatCellsDialog"/>
     </documentControls>
    </qat>
 
  </ribbon>
</customUI>

这里,因为要处理QAT,所以我们从头开始定制用户界面,然后以常规方式设置自定义选项卡和组并在自定义组中添加了两个按钮,将自定义组的getVisible属性设置为False,使其不会在选项卡中显示。最后,在QAT标签中,添加了两个自定义组,然后使用通用的control对象来引用想在QAT中显示的自定义组,同时为组赋予了内置的图像。
注意,有时虽然我们在QAT中定制了组,但打开Excel时不会出现,这是QAT中的一个“小问题”,后文将给出解决方法。
重利用QAT控件
当重利用QAT中的控件时,实际上重利用与之相关的命令,然后作为控件在QAT中添加相同的命令。
重利用的一个主要优势是会对该控件产生全局影响。
下面的XML代码重利用Excel中的两个控件——打开和保存:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
    onLoad="rxIRibbonUI_onLoad">
    <commands>
     <command
      idMso="FileSave"
      onAction="rxFileSave_repurpose"/>
     <command
      idMso="FileOpen"
      onAction="rxFileOpen_repurpose"/>
    </commands>
 
   <ribbon startFromScratch="true">
     <qat>
     <documentControls>
	<control 
       idMso="FileSave"
       screentip="Repurposed Save"
       supertip="This is a repurposed command"/>
 
      <control
       idMso="FileOpen"
       screentip="Repurposed File Open"
       supertip="This is a repurposed command"/>
     </documentControls>
    </qat>
  </ribbon>
 
</customUI>

首先,声明希望重利用的命令并赋宏给每个控件,接着在ribbon标记里定义希望在QAT中出现的命令。
注意,这将产生全局影响,也就是说,如果在该命令出现的任一位置单击该命令或者使用指向该命令的快捷键(这里是Ctrl+o和Ctrl+s),该命令将指向赋值给onAction属性的回调。
在Excel中还可以使用不同的方式。Excel有一个名为OnKey的便捷的方法,当按下指定的键或键组合时触发。这是一个应用程序级的方法,因此一旦在某工作簿中禁用了某命令,所有在相同会话中打开的其它工作簿都将禁用该命令。
因此,在Excel中,如果仅仅需要取消包含UI的工作簿中的快捷键,那么在移动到另一个工作簿中时或者当打开工作簿时需要撤销该快捷键的取消。因为这是一个应用程序级的事件,所以需要使用类模块来监控并响应在工作簿间的转换。
在Excel项目中添加一个类模块并命名,本例中命名为clsAppExcelEvents,输入下面的代码:

Public WithEvents appXL As Excel.Application
 
Private Sub ShortcutsEnabled(ByVal blnEnabled As Boolean)
    Select Case blnEnabled
        Case Is = True
            Application.OnKey "^o"
            Application.OnKey "^s"
        Case Is = False
            Application.OnKey "^o", "commandDisabled"
            Application.OnKey "^s", "commandDisabled"
    End Select
End Sub
 
Private Sub setEnabled(ByVal Wb As Workbook)
    Select Case Wb.Name
        Case Is = ThisWorkbook.Name
            ShortcutsEnabled False
        Case Else
            ShortcutsEnabled True
    End Select
End Sub

注意,在类模块的声明部分声明Excel应用程序。有两个程序来实现这项任务:一个程序检查哪个工作簿是活动工作簿,另一个程序指定OnKey方法。OnKey方法的键组合字符之后,是程序名commandDisabled,该程序必须放置在标准模块中。
在类模块中,可以指定监控的事件。例如,可以监控某工作簿的激活或失活,决定是否取消快捷键:

Private Sub appXL_WorkbookActivate(ByVal Wb As Workbook)
    setEnabled Wb
End Sub
 
Private Sub appXL_WorkbookDeactivate(ByVal Wb As Workbook)
    setEnabled Wb
End Sub

最后,需要在工程打开时设置类,这由包含该工程的工作簿的Open事件来实现:

Dim XL As New clsAppExcelEvents
Private Sub Workbook_Open()
    Set XL.appXL = Application
End Sub

使用表驱动(Table-Driven)方式定制QAT
下图是一个自定义QAT的示例,使用表装载详细信息到QAT中。
QAT4
首先,编写包含UI和QAT菜单按钮的XML代码,这里创建的是文档控件按钮:

     <documentControls>
	<control
	  id="rxgrp"
        imageMso="AdvancedFileProperties"/>
      <button 
	  id="rxbtnShowPopup"
        image="rob"
	  screentip="This is Robert's QAT"
	  supertip="You can only customize the QAT by starting from scratch. If you do not do that you will not be able to make any changes..."
	  onAction="rxbtnShowPopup_Click"
	  />
     </documentControls>

上述XML代码将产生上图所示的两个QAT按钮,这里的关键是赋给onAction属性的回调,单击该按钮后将显示菜单。
接着,创建包含菜单信息的表,如下图所示。
QAT5
上图所示的表只是一个建议,因为您可以在其中添加更多的选项。现在,使用VBA阅读该表并创建菜单:

Public Const POPNAME As String = "MY POPUP"
 
Sub loadPopup()
    Dim mnuWs           As Worksheet
    Dim cmdbar          As CommandBar
    Dim cmdbarPopup     As CommandBarPopup
    Dim cmdbarBtn       As CommandBarButton
    Dim nRowCount       As Long
 
    Call unloadPopup
    Set mnuWs = ThisWorkbook.Sheets("MenuItems")
    Set cmdbar = Application.CommandBars.Add(POPNAME, msoBarPopup)
 
    nRowCount = 2
    With mnuWs
        Do Until IsEmpty(.Cells(nRowCount, 1))
 
            Select Case UCase(.Cells(nRowCount, 1))
                Case "POPUP"
                    Set cmdbarPopup = cmdbar.Controls.Add(msoControlPopup)
                        cmdbarPopup.Caption = .Cells(nRowCount, 2)
                        If .Cells(nRowCount, 3) <> "" Then
                            cmdbarPopup.BeginGroup = True
                        End If
 
                Case "BUTTON"
                    Set cmdbarBtn = cmdbarPopup.Controls.Add(msoControlButton)
                        cmdbarBtn.Caption = .Cells(nRowCount, 2)
                        If .Cells(nRowCount, 3) <> "" Then
                            cmdbarBtn.BeginGroup = True
                        End If
                        cmdbarBtn.FaceId = .Cells(nRowCount, 4)
                        cmdbarBtn.OnAction = .Cells(nRowCount, 5)
 
                Case "BUTTON_STANDALONE"
                    Set cmdbarBtn = cmdbar.Controls.Add(msoControlButton)
                        cmdbarBtn.Caption = .Cells(nRowCount, 2)
                        If .Cells(nRowCount, 3) <> "" Then
                            cmdbarBtn.BeginGroup = True
                        End If
                        cmdbarBtn.FaceId = .Cells(nRowCount, 4)
                        cmdbarBtn.OnAction = .Cells(nRowCount, 5)
 
            End Select
            nRowCount = nRowCount + 1
        Loop
    End With
 
End Sub
 
Sub unloadPopup()
    On Error Resume Next
    Application.CommandBars(POPNAME).Delete
End Sub
 
Sub showAbout()
    MsgBox "This is a sample on how to customize the QAT on the fly!!", vbInformation
End Sub
 
Sub showHelp()
    On Error GoTo Err_Handler
    ThisWorkbook.FollowHyperlink "http://www.msofficegurus.com", , True, True
    Exit Sub
 
Err_Handler:
    MsgBox Err.Description, vbCritical, Err.Number
 
End Sub

最后,需要编写回调的代码。使用onLoad事件调用loadPopup过程,以便创建弹出菜单,并准备当在QAT中单击该按钮时使用,也包含当发生单击时显示弹出菜单的单击事件代码:

Dim grxIRibbonUI        As IRibbonUI
 
Sub rxIRibbonUI_onLoad(ribbon As IRibbonUI)
    On Error Resume Next
    Set grxIRibbonUI = ribbon
 
    Application.Workbooks.Add
    If ActiveWorkbook.Name <> ThisWorkbook.Name Then
        With ActiveWorkbook
            .Saved = True
            .Close
        End With
    End If
 
'   可以在这个事件或者ThisWorkbook的Open事件中装载弹出菜单
    Call loadPopup
End Sub
 
Sub rxbtnShowPopup_Click(control As IRibbonControl)
    On Error Resume Next
    Application.CommandBars(POPNAME).ShowPopup
End Sub
 
Sub rxbtnHappy_Click(control As IRibbonControl)
    MsgBox "This is Mr. Happy Face... hurray!!", vbExclamation
End Sub

定制QAT时的一些注意事项
虽然在QAT中可以方便地实现自定义,但也有一些缺陷。
(1)无法装载控件
上文中曾经谈到,在定制好后,例如按钮和组,打开工作簿时,却发现定制的控件没有出现。这种情况在使用sharedControls时非常普遍。
一种解决方法是先最小化工作簿,然后再最大化,通过刷新来使定制的控件出现;或者再打开一个工作簿后,将其关闭,看看定制的控件是否出现。
(2)无法为控件装载自定义图像
共享控件的表现通常无法预料,并且不能提供可信赖且一致的界面,因此建议在共享控件中尽量不要使用自定义图像。
至于文档控件,可以使用下面的过程刷新包含UI的窗口来解决此类问题:

Sub rxIRibbonUI_onLoad(ribbon As IRibbonUI)
    Set grxIRibbonUI = ribbon
    On Error Resume Next
    Application.Workbooks.Add
    If ActiveWorkbook.Name <> ThisWorkbook.Name Then
        With ActiveWorkbook
            .Saved = True
            .Close
        End With
    End If
End Sub

上述技巧也能用于无法装载自定义控件中。
(3)复制控件
在QAT中控件的复制通常发生在工作簿或文档之间切换时。假设有一个包含定制的QAT的工作簿,当按Alt+Tab移动到另一个文档,然后返回定制的工作簿时,在QAT中的控件被复制、三次复制、四次复制……这种复制能够被传播到没有包含任何XML定制的其它工作簿和文档。
此时,需要关闭后重新打开文档才能消除这种不应有的复制。

相关文章

表驱动的方式构建自定义命令栏(示例2)

1 颗星2 颗星3 颗星4 颗星5 颗星 (目前还没有人投票)
Loading ... Loading ...

下面是John Walkenbach介绍的创建自定义菜单的技巧,辑录于此。
Excel 97-2003使用CommandBars对象来构建菜单,必须使用VBA创建特定工作簿的菜单,下面的技巧介绍了一种创建自定义菜单的相对简单的方法。当特定的工作簿打开时,出现自定义菜单;当关闭该工作簿时,删除自定义菜单。
示例中,包含了用于创建自定义菜单的所有VBA代码,在大多数情况下,不需要修改这些代码,而只需要简单地修改MenuSheet工作表。
注意,这里介绍的技术只是新建菜单,而不会在已有的菜单中添加菜单项。
MenuSheet工作表
这项技术使用了一个Excel工作表,这里将其命名为MenuSheet。要创建自定义菜单,只需简单地修改表中的数据。MenuSheet工作表如下图所示。
makemenu1
工作表MenuSheet包含5列:

  • 菜单层级:特定项目的层级。有效值为1、2、3。层级1代表菜单;2代表菜单项;3代表子菜单项。通常,有一个层级1的项目,在它下面是层级2的项目。层级2的项目可以有也可以没有层级3的项目(子菜单)。
  • 标题:菜单、菜单项或子菜单显示的文本。使用符号(&)指定带下划线的字符,即快捷键。
  • 位置/宏:对于层级1的项目,应该是一个表示在菜单栏中位置的整数。对于层级2或层级3的项目,应该是选择该项目时执行的宏的名称。如果层级2的项目有一个或多个层级3的项目,那么层级2的项目可能没有与之相关的宏。
  • 分隔线:如果为True,则在该菜单项或子菜单项之前应该放置一条分隔线。
  • FaceID:可选的。代表显示在菜单项旁边的内置图像的编号。

菜单示例
使用上图所示表所创建的菜单如下图:
makemenu2
代码清单

Sub CreateMenu()
'   打开工作簿时执行本过程
'   注: 在本过程中没有编写错误处理的代码

    Dim MenuSheet As Worksheet
    Dim MenuObject As CommandBarPopup
 
    Dim MenuItem As Object
    Dim SubMenuItem As CommandBarButton
    Dim Row As Integer
    Dim MenuLevel, NextLevel, PositionOrMacro, Caption, Divider, FaceId
 
''''''''''''''''''''''''''''''''''''''''''''''''''''
'   菜单数据所在的工作表
    Set MenuSheet = ThisWorkbook.Sheets("MenuSheet")
''''''''''''''''''''''''''''''''''''''''''''''''''''

'   确保不会出现重复菜单
    Call DeleteMenu
 
'   初始化行计数器
    Row = 2
 
'   使用存储在MenuSheet工作表中的数据添加菜单,菜单项和子菜单项
    Do Until IsEmpty(MenuSheet.Cells(Row, 1))
        With MenuSheet
            MenuLevel = .Cells(Row, 1)
            Caption = .Cells(Row, 2)
            PositionOrMacro = .Cells(Row, 3)
            Divider = .Cells(Row, 4)
            FaceId = .Cells(Row, 5)
            NextLevel = .Cells(Row + 1, 1)
        End With
 
        Select Case MenuLevel
            Case 1 ' 代表菜单
'              在工作表命令栏中添加顶级菜单
                Set MenuObject = Application.CommandBars(1). _
                    Controls.Add(Type:=msoControlPopup, _
                    Before:=PositionOrMacro, _
                    Temporary:=True)
                MenuObject.Caption = Caption
 
            Case 2 ' 代表菜单项
                If NextLevel = 3 Then
                    Set MenuItem = MenuObject.Controls.Add(Type:=msoControlPopup)
                Else
                    Set MenuItem = MenuObject.Controls.Add(Type:=msoControlButton)
                    MenuItem.OnAction = PositionOrMacro
                End If
                MenuItem.Caption = Caption
                If FaceId <> "" Then MenuItem.FaceId = FaceId
                If Divider Then MenuItem.BeginGroup = True
 
            Case 3 ' 代表子菜单项
                Set SubMenuItem = MenuItem.Controls.Add(Type:=msoControlButton)
                SubMenuItem.Caption = Caption
                SubMenuItem.OnAction = PositionOrMacro
                If FaceId <> "" Then SubMenuItem.FaceId = FaceId
                If Divider Then SubMenuItem.BeginGroup = True
        End Select
        Row = Row + 1
    Loop
End Sub
 
Sub DeleteMenu()
'   关闭工作簿时执行本过程
'   删除创建的菜单
    Dim MenuSheet As Worksheet
    Dim Row As Integer
    Dim Caption As String
 
    On Error Resume Next
    Set MenuSheet = ThisWorkbook.Sheets("MenuSheet")
    Row = 2
    Do Until IsEmpty(MenuSheet.Cells(Row, 1))
        If MenuSheet.Cells(Row, 1) = 1 Then
            Caption = MenuSheet.Cells(Row, 2)
            Application.CommandBars(1).Controls(Caption).Delete
        End If
        Row = Row + 1
    Loop
    On Error GoTo 0
End Sub
 
Sub DummyMacro()
    MsgBox "这里一个用于演示的宏."
End Sub

代码简要解释

  • CommandBars(1)引用“工作表菜单栏”,也可以通过名称“Worksheet Menu Bar”来引用,其中1代表“工作表菜单栏”在CommandBars集合中的索引。
  • CommandBars集合是所有CommandBar对象的集合,而每个CommandBar对象都有一个 Controls集合。
  • Add方法向Controls集合中添加新控件。参数Type为msoControlPopup时,指定控件类型为弹出式控件;参数Before指定所添加的控件的位置;参数Temporary设置为True,表示为临时命令栏。
  • FaceID属性确写出现在菜单文本旁的图像,其中的数字代表内置图像编号。
  • BeginGroup属性设置为True时,将在该菜单项前放置分隔条。

技术运用
按下列步骤在工作簿或加载项中使用这项技术:
1、在VBE中插入一个标准模块,将上述代码复制到该模块中。
2、在ThisWorkbook模块中,编写下列代码:

Private Sub Workbook_Open()
    Call CreateMenu
End Sub
 
Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Call DeleteMenu
End Sub

在工作簿打开时执行Workbook_Open事件,在工作簿关闭时执行Workbook_BeforeClose事件。
3、在工作簿中插入一个新工作表并命名为MenuSheet。按上图所示的格式输入菜单数据,或者直接复制上述数据后,再进行修改。

相关文章

表驱动的方式构建自定义命令栏

1 颗星2 颗星3 颗星4 颗星5 颗星 (目前还没有人投票)
Loading ... Loading ...

在Excel中采用表驱动(table-driven)的方式创建自定义命令栏是一种常用的技术。即在工作表中设置适当的数据,然后利用这些数据来创建菜单,这样既简单又方便。而且,不懂VBA编程的用户也能通过修改工作表中的数据来创建自已的命令栏。
这里介绍一个相对简单的例子,来自于《Mastering Excel 2003 Programming with VBA》。在下图所示的工作表中放置用来创建菜单的数据:
menubuilder1
现在,需要使用程序来运用Menu Builder工作表来构建适当的菜单,除了ParentTag列(即A列)外,其他列为CommandBarControl对象的不同属性。ParentTag列用来指定是否创建一个新的菜单、菜单项或子菜单。
创建的菜单如下图。
menubuilder2
代码如下:

Const NA = "N/A"
'列偏移量
Const TAG_OFFSET = 1
Const CAPTION_OFFSET = 2
Const TYPE_OFFSET = 3
Const ONACTION_OFFSET = 4
Const BEGINGROUP_OFFSET = 5
Const DESCRIPTION_OFFSET = 6
 
Sub BuildMenu()
  Dim ws As Worksheet
  Dim rg As Range
  On Error GoTo ErrHandler
  Set ws = ThisWorkbook.Worksheets("Menu Builder")
  '从第二行开始,因为第一行已经包含了列标题
  Set rg = ws.Cells(2, 1)
  Do Until IsEmpty(rg)
      If rg.Value = NA Then
      '新建顶级菜单项
          AddTopLevelItem rg
      Else
          '现有控件的子菜单
          AddSubItem rg
      End If
      '向下移一行
      Set rg = rg.Offset(1, 0)
  Loop
ExitPoint:
  Set rg = Nothing
  Set ws = Nothing
  Exit Sub
ErrHandler:
  Debug.Print Err.Description
  Resume ExitPoint
End Sub
 
'向工作表菜单栏中添加新菜单项
Private Function AddTopLevelItem(rg As Range) As CommandBarControl
  Dim cbWSMenuBar As CommandBar
  Dim cbc As CommandBarControl
  On Error GoTo ErrHandler
  Set cbWSMenuBar = Application.CommandBars("Worksheet Menu Bar")
  '添加菜单项
  Set cbc = cbWSMenuBar.Controls.Add(msoControlPopup, , , , True)
  cbc.Tag = rg.Offset(0, TAG_OFFSET).Value
  cbc.DescriptionText = rg.Offset(0, DESCRIPTION_OFFSET).Value
  cbc.Caption = rg.Offset(0, CAPTION_OFFSET).Value
  '返回新添加的菜单项
  Set AddTopLevelItem = cbc
ExitPoint:
  Set cbc = Nothing
  Set cbWSMenuBar = Nothing
  Exit Function
ErrHandler:
  Set AddTopLevelItem = Nothing
  Resume ExitPoint
End Function
 
Private Function AddSubItem(rg As Range) As CommandBarControl
  Dim cbcParent As CommandBarControl
  Dim cbc As CommandBarControl
  On Error GoTo ErrHandler
  '基于父标记定位父菜单
  Set cbcParent = Application.CommandBars.FindControl(, , rg.Value)
  If Not cbcParent Is Nothing Then
      '添加菜单项
      Set cbc = cbcParent.Controls.Add(GetType(rg))
      '确保该菜单项具有一个OnAction值而不是N/A
      If rg.Offset(0, ONACTION_OFFSET).Value <> NA Then
          cbc.OnAction = rg.Offset(0, ONACTION_OFFSET).Value
      End If
      cbc.Tag = rg.Offset(0, TAG_OFFSET).Value
      cbc.DescriptionText = rg.Offset(0, DESCRIPTION_OFFSET).Value
      cbc.Caption = rg.Offset(0, CAPTION_OFFSET).Value
      cbc.BeginGroup = rg.Offset(0, BEGINGROUP_OFFSET).Value
      '返回新添加的控件
      Set AddSubItem = cbc
  Else
      '不能找到父控件-返回无
      Set AddSubItem = Nothing
  End If
ExitPoint:
  Set cbc = Nothing
  Set cbcParent = Nothing
  Exit Function
ErrHandler:
  Debug.Print Err.Description
  Set AddSubItem = Nothing
  Resume ExitPoint
End Function
 
'将所选的msoControlType枚举转换为值
Private Function GetType(rg As Range) As Long
  Dim sType As String
  sType = rg.Offset(0, TYPE_OFFSET).Value
  Select Case sType
      Case Is = "msoControlPopup"
          GetType = msoControlPopup
      Case Is = "msoControlButton"
          GetType = msoControlButton
      Case Is = "msoControlDropDown"
          GetType = msoControlDropdown
      Case Else '包括N/A
      '默认为msoControlPopup
          GetType = msoControlPopup
  End Select
End Function
 
'删除标记为"我的菜单1"的控件
Sub DeleteMyMenu1()
  DeleteMenu "我的菜单1"
End Sub
 
'删除标记为"我的菜单2"的控件
Sub DeleteMyMenu2()
  DeleteMenu "我的菜单2"
End Sub
 
Private Sub DeleteMenu(sTag As String)
  Dim cbc As CommandBarControl
  Set cbc = Application.CommandBars.FindControl(Tag:=sTag)
  If Not cbc Is Nothing Then
  cbc.Delete
  End If
  Set cbc = Nothing
End Sub

上述代码的主程序是BuildMenu过程,该过程遍历工作表MenuBuilder中A列的单元格,检查其值是否等于N/A,如果等于则调用AddTopLevelItem过程在工作表菜单栏中创建一个新菜单项,如果不等于则调用AddSubItem过程,根据标记查找已存在的菜单项并在其中添加子菜单项。为了找到控件,代码使用FindControl方法。
此外,AddTopLevelItem过程忽略了Type列和OnAction列,并自动添加msoControlPopup类型的控件。AddSubItem过程将检查OnAction列的值,确保其中不会包含值N/A,若包含则不会设置OnAction属性。
最后的三个过程用来删除已创建好的菜单项。
前面说过,这是一个简单的菜单构建器示例,其中只包括了构建菜单所需的常见的一些属性。您可以添加列并修改相应的代码,使其功能更加强大,例如添加控件的Visible和Enabled属性列。

相关文章