使Office按钮闪烁

1 颗星2 颗星3 颗星4 颗星5 颗星 (1 人投票, 平均: 5.00 out of 5)
Loading ... Loading ...

这是在MS Office Gurus中看到的一个很有趣的技巧,但说实在的,不怎么实用。既然有趣,那还是介绍一下吧。
在Office 2007中,Microsoft引入了“Office按钮”,位于界面的左上角,其功能与以前版本的“文件”菜单相似。当单击该按钮时,会出现处理文件的功能,诸如新建、打开、保存、另存为、打印……等等。
使用下面的方法,能够使Office按钮闪烁,以引起用户的注意,如下图所示。
OfficeButtonFlash
一旦在该按钮上单击后,将停止闪烁。在MS Office Gurus中,作者使用一个2分多钟的视频介绍了实现这种效果的方法。这里总结如下:
1、在桌面单击“开始——运行”。
2、输入regedit.exe,打开注册表编辑器。
3、单击HKEY_CURRENT_USER。
4、在左侧的目录树中找到Software\Microsoft\Office\12.0\Common\General。
5、在右侧的窗口中找到OfficeMenuDiscovered并双击,在出现的“编辑DWORD值”对话框中将其值改为0,单击“确定”并退出注册表编辑器。
此时,若开启Office 2007,则Office按钮会闪烁,直到您单击它。这是一次性的操作,即修改该值后第一次开启Office 2007应用程序,会使Office按钮闪烁,单击Office按钮后,停止闪烁,注册表中相应的健值也会恢复,再开启Office应用程序,Office按钮不会再闪烁。

相关文章

禁用最近使用的文件列表

1 颗星2 颗星3 颗星4 颗星5 颗星 (1 人投票, 平均: 5.00 out of 5)
Loading ... Loading ...

在Excel 2003中单击菜单“文件”,在Excel 2007中单击“Office按钮”,都会发现有最近使用的文件(档)列表,您可以在Excel选项中设置希望在这里列出的最近使用的文件数量。在Excel 2003中最多可以列出9篇最近使用的文件名,而在Excel 2007中最多可以列出50篇最近使用的文档。
有时,我们也许想禁用“最近使用的文档”,使得其他用户不知道您最近打开了什么文档。当然,可以在Excel选项中将“最近使用的文档”数设置为0或者取消对该选项的选择,但对于稍微了解Excel操作的用户来说,很容易将这项设置再修改回来。下面介绍一种方法,彻底禁用该选项。
第1步:打开注册表编辑器。单击“开始—运行”,在“运行”对话框中输入“regedit”命令,单击“确定”。
第2步:在注册表编辑器的左侧,导航至
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\
第3步:在注册表编辑器的右侧,单击右键—新建—DWORD值,添加一个DWORD值,将其命名为NoRecentDocsHistory。
第4步:在该值上单击右键—修改,在“编辑DWORD值”对话框中修改数值数据为1,如下图所示,此时,“最近使用的文档”项将被禁用,在“文件”菜单或“Office按钮”里的文档清单也会被清除。若要重新使用“最近使用的文档”项,则将该值设置为0。
disablefilelist1
第5步:关闭注册表编辑器。
此时,“最近使用的文件列表”(Excel 2003)或“最近使用的文档”(Excel 2007)选项将被禁用,用户不会再看到最近打开的文档列表了。
disablefilelist2
图:Excel 2003中的“选项”对话框
disablefilelist3
图:Excel 2007中的“Excel选项”对话框

相关文章

图解VSTO安装

1 颗星2 颗星3 颗星4 颗星5 颗星 (3 人投票, 平均: 4.67 out of 5)
Loading ... Loading ...

很多朋友对VSTO的安装都存有一些疑问,特别是如何配置Visual Studio和Office,使VSTO能够正常工作。趁一次重装系统的机会,我将配置VSTO的详细过程记录了下来,供参考。

  • 安装Microsoft Office 2003。
  • 安装Microsoft Office 2007。(两者可同时安装在同一台机子上哦,只不过在双击具体文档启动时可能有点“不理想”)
  • 安装Visual Studio 2005。在Visual Studio 2005中自带有Microsoft Visual Studio 2005 Tools for the Microsoft Office System Runtime、Microsoft Visual Studio 2005 Tools for the Microsoft Office System Runtime语言包、以及Office System 2003主互操作程序集。
    在安装程序完成后,会出现如图1所示的界面。
    VS2005Install1
    图1:VS2005安装完成页
    如果您安装的是Microsoft Office 2003 SP2或SP3,不需要理会这里面列出的警告,直接单击“完成”按钮即可。
  • 在“控制面板”中单击“添加/删除程序”。在“添加或删除程序”列表中找到“Microsoft Office Professional Edition 2003”并单击其右侧的“更改”按钮。
    如图2所示,在“Microsoft Office 2003安装”中,选取“选择应用程序的高级自定义”前的复选框,单击“下一步”。
    VS2005Install2
    图2:Microsoft Office 2003安装界面1
  • 如图3所示,单击所选Office应用程序前面的下拉箭头,找到.NET可编程性支持并选择“从本机运行”。
    VS2005Install3
    图3:Microsoft Office 2003安装界面2
    完成后,单击“更新”按钮,出现更新Office的界面。更新完成后,弹出如图4所示的对话框。
    VS2005Install4
    图4:Microsoft Office 2003安装界面3
  • 开启Visual Studio 2005,选择“新建项目”命令,选择“Office”节点及相应的模板,单击“确定”,如图5所示。
    VS2005Install5
    图5:Visual Studio 2005新建项目
    出现如图6所示的对话框,单击“确定”按钮。
    VS2005Install6
    图6:VSTO项目向导
    此时,会出现如图7所示的警告信息,单击“确定”按钮。否则,将不能成功创建项目。
    VS2005Install7
    图7:第一次创建VSTO项目时的警告信息
  • 成功创建的Excel项目如图8所示。
    VS2005Install8
    图8:创建成功的Excel项目
    在Office 2007发布后不久,Microsoft又发布了VSTO 2005 SE,用于创建支持Office 2007的项目。
  • 安装Office 2007 Primary Interop Assembly(即Office 2007主互操作程序集)。
  • 安装VSTO 2005 SE,安装界面如图9所示。注意,如果您使用的是中文版的Visual Studio,那么要下载中文的安装包。
    VS2005Install9
    图9:安装Visual Studio 2005 Tools for Office Second Edition
  • 在“控制面板”中单击“添加/删除程序”。在“添加或删除程序”列表中找到“Microsoft Office Enterprise 2007”并单击其右侧的“更改”按钮。在图10所示的界面中,选择“添加或删除功能”,单击“继续”按钮。
    VS2005Install10
    图10:修改Microsoft Office Enterprise 2007安装
  • 与前述步骤5一样,启动.NET可编程性支持,如图11所示。
    VS2005Install11
    单击“继续”。
  • 重新启动Visual Studio 2005,此时的Office节点界面除图5所列的模板外(在Office节点上单击时列出),还增加了外接程序模板,如图12所示。
    VS2005Install12
    图12:VS 2005 SE中新增的模板
  • 好了!大致过程就这些,可能各人的机子稍有不同,灵活设置吧!

后注:现在Microsoft已经推出了Visual Studio 2008,在《MSDN Magazine》上有一篇关于VSTO 3.0的文章:《VSTO3.0:用Visual Studio 2008开发Office业务应用程序》。文章链接:

http://msdn.microsoft.com/msdnmag/issues/08/LA/ExtendingOutlookWithVS08/default.aspx?loc=zh

Visual Studio 2008 真正突出的一个特点是支持 Microsoft® Office 解决方案开发。
您也可下载 VSTO Second Edition 开发 Office 2003 应用程序和 2007 Microsoft Office 系统的解决方案。但是,在 Visual Studio 2008 中包含了所有的 VSTO 项目模板。

相关文章

复制用户窗体文本框里的数据

1 颗星2 颗星3 颗星4 颗星5 颗星 (3 人投票, 平均: 3.67 out of 5)
Loading ... Loading ...

通常,我们可以在选择要复制的文本后,按Ctrl+C组合键来复制文本。下面通过两个简单的示例介绍如何使用VBA代码复制用户窗体文本框中的文本数据。
示例一:如下图1所示,在示例窗体中有一个文本框和一个命令按钮。当窗体被激活时,文本框中自动显示文字“完美Excel”,单击“复制文本”按钮后,将文本框中的数据复制到剪贴板。
copytextfromtextbox1
图1:带有文本框和命令按钮的用户窗体
首先,按图1设计好用户窗体界面。然后,在该用户窗体模块中,输入下列代码:

Dim myClipboard As New DataObject
 
Private Sub UserForm_Activate()
    Me.TextBox1.Value = "完美Excel"
End Sub
 
Private Sub CommandButton1_Click()
    With myClipboard
        .SetText Me.TextBox1.Text
        .PutInClipboard
    End With
End Sub

示例二:在用户窗体中有多个文本框,要求单击按钮后将含有数据的文本框中的数据全部复制到剪贴板。用户窗体如下图2所示,含有6个文本框。
copytextfromtextbox2
图2:带有多个文本框的用户窗体
首先,按图2设计好用户窗体界面。然后,在用户窗体模块中,输入下列代码:

Dim strUnion As String
 
Private Sub CommandButton1_Click()
    Dim dObj As DataObject
    With Me
        If Not .TextBox1 = Empty Then strUnion = strUnion & .TextBox1
        If Not .TextBox2 = Empty Then strUnion = strUnion & vbCrLf & .TextBox2
        If Not .TextBox3 = Empty Then strUnion = strUnion & vbCrLf & .TextBox3
        If Not .TextBox4 = Empty Then strUnion = strUnion & vbCrLf & .TextBox4
        If Not .TextBox5 = Empty Then strUnion = strUnion & vbCrLf & .TextBox5
        If Not .TextBox6 = Empty Then strUnion = strUnion & vbCrLf & .TextBox6
    End With
 
    Set dObj = New DataObject
 
    dObj.SetText strUnion, 1
    dObj.PutInClipboard
End Sub

说明与扩展
下面对示例中涉及到的主要对象及其属性、方法进行简单的介绍和整理,并进行一些扩展(也可参考VBA帮助)。
本文虽然题为复制文框框中的数据,但DataObject对象也可应用到其他的用户窗体控件中。

  • DataObject 对象
    DataObject对象能够像“剪贴板”一样保存一段文本格式的文本,并能为每种附加的文本格式(例如用户定义的格式)保存一段文本。
    DataObject对象的工作方式与“剪贴板”很相像。如果将一串文本复制到DataObject对象,DataObject 对象将保存该文本。如果将相同格式的第二段文本复制到该DataObject对象,则DataObject对象将清除前一文本,并保存第二段的文本。对于每种指定的格式,它保存一段从最近一次操作得到的文本。
    但DataObject对象又与“剪贴板”不同,DataObject支持与“剪贴板”及文本的拖放操作有关的命令。在开始进行与“剪贴板”有关的操作(如GetText)或拖放操作时,与该操作有关的数据会被移到DataObject对象中。
    DataObject对象具有下列方法:

    • SetText方法:使用特定的格式复制文本串到DataObject对象中。
    • GetText方法:使用特定的格式在DataObject对象中检索文本串。
    • GetFormat方法:返回的值表示DataObject对象中是否有指定的格式。
    • StartDrag方法:初始化针对DataObject的拖放操作。
    • GetFromClipboard:从剪贴板复制数据到DataObject对象。
    • PutInClipboard:将数据从DataObject对象移到剪贴板上。
    • Clear方法:删除DataObject对象中的所有对象。
  •  

  • GetFormat方法、GetText方法 和SetText方法示例
    本示例使用GetFormat方法、GetText方法和SetText方法在DataObject对象和剪贴板之间传输文本。
    示例窗体如下图3所示,带有一个文本框TextBox1,四个命令按钮CommandButton1、CommandButton2、CommandButton3和CommandButton,以及一个标签Label1。用户把文本键入文本框然后单击命令按钮CommandButton1,把文本以标准文本格式传输到DataObject对象。单击命令按钮CommandButton2,在DataObject对象中检索文本。单击命令按钮CommandButton3,用自定义格式把文本从文本框TextBox1复制到DataObject对象。单击命令按钮CommandButton4,用自定义格式在DataObject对象中检索文本。
    copytextfromtextbox3
    图3:演示GetFormat、GetText、SetText方法的用户窗体
    窗体模块中的代码为:

    Dim MyDataObject As DataObject
     
    Private Sub CommandButton1_Click()
        '把标准格式放到剪贴板上
        If TextBox1.TextLength > 0 Then
            Set MyDataObject = New DataObject
            MyDataObject.SetText TextBox1.Text
            Label1.Caption = "放置到DataObject中"
            CommandButton2.Enabled = True
            CommandButton4.Enabled = False
        End If
    End Sub
     
    Private Sub CommandButton2_Click()
        '从剪切板得到标准格式
        If MyDataObject.GetFormat(1) = True Then
            Label1.Caption = "标准格式 - " _
                & MyDataObject.GetText(1)
        End If
    End Sub
     
    Private Sub CommandButton3_Click()
        '把自定义格式放到剪切板上
        If TextBox1.TextLength > 0 Then
            Set MyDataObject = New DataObject
            MyDataObject.SetText TextBox1.Text, 233
            Label1.Caption = "在DataObject中的自定义格式"
            CommandButton4.Enabled = True
            CommandButton2.Enabled = False
        End If
    End Sub
     
    Private Sub CommandButton4_Click()
        '从剪切板得到自定义格式
        If MyDataObject.GetFormat(233) = True Then
            Label1.Caption = "自定义格式 - " _
                & MyDataObject.GetText(233)
        End If
    End Sub
     
    Private Sub UserForm_Initialize()
        CommandButton2.Enabled = False
        CommandButton4.Enabled = False
    End Sub
  •  

  • Copy方法、GetFromClipboard方法、GetText方法和DataObject对象示例
    本示例演示从文本框到剪贴板,从剪贴板到DataObject对象以及从DataObject对象到另一个文本框的数据移动。GetFromClipboard方法把数据从剪贴板传输到DataObject对象。该示例还使用了Copy方法和GetText方法。
    示例窗体如下图4所示,带有两个文本框和一个命令按钮。单击命令按钮,将把文本从一个文本框移至另一个文本框。
    copytextfromtextbox4
    图4:Copy、GetFromClipboard、GetText和DataObject对象示例窗体
    窗体模块中的代码如下:

    Dim MyData as DataObject
     
    Private Sub CommandButton1_Click()
        '在把文本复制到剪切板之前,需要选定一段文本
        TextBox1.SelStart = 0
        TextBox1.SelLength = TextBox1.TextLength
        TextBox1.Copy
     
        MyData.GetFromClipboard
        TextBox2.Text = MyData.GetText(1)
    End Sub
     
    Private Sub UserForm_Initialize()
        Set MyData = New DataObject
        TextBox1.Text = "移动数据到剪贴板,然后到DataObject,接着到另一文本框!"
    End Sub
  •  

  • 列表框控件、DataObject对象、MouseMove事件和StartDrag方法、SetText方法示例
    本示例使用DataObject对象来包含拖动文本,演示从一个列表框到另一个列表框的拖放操作。在MouseMove事件中使用SetText方法和StartDrag方法来完成拖放操作。
    示例窗体如下图5所示,带有两个列表框。
    copytextfromtextbox5
    图5:从一个列表框中拖动数据并放置到另一个列表框中
    窗体模块代码如下:

    Private Sub ListBox2_BeforeDragOver(ByVal Cancel As _
        MSForms.ReturnBoolean, ByVal Data As _
        MSForms.DataObject, ByVal X As Single, _
        ByVal Y As Single, ByVal DragState As Long, _
        ByVal Effect As MSForms.ReturnEffect, _
        ByVal Shift As Integer)
        Cancel = True
        Effect = 1
    End Sub
     
    Private Sub ListBox2_BeforeDropOrPaste(ByVal _
        Cancel As MSForms.ReturnBoolean, _
        ByVal Action As Long, ByVal Data As _
        MSForms.DataObject, ByVal X As Single, _
        ByVal Y As Single, ByVal Effect As _
        MSForms.ReturnEffect, ByVal Shift As Integer)
        Cancel = True
        Effect = 1
        ListBox2.AddItem Data.GetText
    End Sub
     
    Private Sub ListBox1_MouseMove(ByVal Button As _
         Integer, ByVal Shift As Integer, ByVal X As _
         Single, ByVal Y As Single)
        Dim MyDataObject As DataObject
        If Button = 1 Then
            Set MyDataObject = New DataObject
            Dim Effect As Integer
            MyDataObject.SetText ListBox1.Value
            Effect = MyDataObject.StartDrag
        End If
    End Sub
     
    Private Sub UserForm_Initialize()
        Dim i As Long
        For i = 1 To 10
            ListBox1.AddItem "Choice " _
                & (ListBox1.ListCount + 1)
        Next i
    End Sub
  •  

  • DataObject对象和“剪贴板”的区别
    DataObject对象和“剪贴板”都提供了将数据从一处移到另一处的方法。作为应用程序开发人员,无论是使用DataObject对象还是使用“剪贴板”,都要记住下列几项要点:

    • 只要各项数据具有不同的数据格式,DataObject对象和“剪贴板”就都可以同时保存多项数据。但是如果已经有数据使用了要保存数据的格式,则保存这个新数据,并丢弃原来的数据。
    • “剪贴板”支持图片格式和文本格式。而DataObject对象目前只支持文本格式。
    • 只有应用程序运行时,DataObject对象才存在;而“剪贴板”则是始终运行的(与正在运行的操作系统同时存在)。这意味着,将数据放到“剪贴板”后,如果关闭了应用程序,“剪贴板”上的数据不会丢失。DataObject对象则没有这个功能。如果关闭应用程序,该应用程序放置在DataObject对象上的数据就会丢失。
    • DataObject对象是一个标准的OLE对象,而“剪贴板”不是。这意味着,“剪贴板”支持标准的移动操作(复制、剪切和粘贴),但不支持拖放操作。如果需要应用程序支持拖放操作,就必须使用DataObject对象。
    • 提示 当用SetText方法将数据移动到“剪贴板”或DataObject对象时,可定义自己的数据格式名称。这样便于区分是应用程序移动的文本还是用户移动的文本。

示例文档下载:

相关文章

编写高效Excel VBA代码的最佳实践

1 颗星2 颗星3 颗星4 颗星5 颗星 (12 人投票, 平均: 4.67 out of 5)
Loading ... Loading ...

很多Excel VBA文章和图书都介绍过如何优化VBA代码,使代码运行得更快。下面搜集了一些使Excel VBA代码运行更快的技术和技巧,基本上都是实践经验的总结。如果您还有其它优化Excel VBA代码的方法,可以在本文后留言或给出链接,与大家分享。
对于应用程序属性,在代码运行时关闭除必需属性以外的其它所有属性
在代码运行时关闭不需要的Excel功能。其原因是,如果通过VBA更新不同的单元格区域,或者从不同的单元格区域复制/粘贴来创建汇总表,则不希望Excel浪费时间和资源来重新计算公式、显示粘贴进度或者重绘网格,尤其在每次单独的操作后(更有甚者,如果代码使用了循环,则每次单独操作后Excel都会在后台运行这些功能)。只需要在代码执行结束时进行一次重新计算和重绘就足以使工作簿更新。
下面的代码将帮助您提高代码的执行速度。
(1)放置在主代码前的一段代码,获取Excel当前的属性状态,然后将其关闭

'获得当前的Excel设置状态,将其放置在代码的开头
screenUpdateState = Application.ScreenUpdating
statusBarState = Application.DisplayStatusBar
calcState = Application.Calculation
eventsState = Application.EnableEvents
displayPageBreakState = ActiveSheet.DisplayPageBreaks '注:这是工作表级的设置
'关闭一些Excel功能使代码运行更快
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False '注:这是工作表级的设置

(2)放置在主代码结束后的一段代码,用来将Excel恢复到代码运行前的设置

'代码运行后,恢复Excel原来的状态;将下面的代码放在代码的末尾
Application.ScreenUpdating = screenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
ActiveSheet.DisplayPageBreaks = displayPageBreaksState '注:这是工作表级的设置

下面简要解释这些设置:
Application.ScreenUpdating:将该属性设置为False,告诉Excel不要重绘屏幕。其优点是不需要Excel花费资源来绘制屏幕,因而其改变会更快而不致让用户察觉其变化。因为如此频繁地绘制屏幕需要大量的资源,所以关闭绘制屏幕直到代码执行结束。在代码结束前,确保重新开启了该属性。
Application.DisplayStatusBar:将该属性设置为False,告诉Excel停止显示状态栏。例如,如果使用VBA复制/粘贴单元格,当粘贴执行时Excel将在状态栏中显示操作的进度。关闭屏幕更新不会关闭状态栏显示,因此,如果需要的话,可以禁用屏幕更新而仍然可以通过状态栏给用户提供反馈。记住,如果将该属性设置为False,在代码结束前应该将其设置为True。
Application.Calculation:该属性允许编程设置Excel的计算模式。“手工的”(xlCalculationManual)模式意味着Excel等待用户(或代码)来触发计算;默认为“自动的”(xlCalculationAutomatic)模式,意味着由Excel来决定何时重新计算工作簿(例如,当在工作表中输入新公式时)。由于重新计算工作簿将花费时间且浪费资源,因此可能不希望每次改变单元格值时Excel都触发重新计算。当代码执行时关闭重新计算,在代码结束前再设置回重新计算模式。
Application.EnableEvents:将该属性设置为False,告诉Excel不要触发事件。你可能不希望Excel为每个正在通过代码发生改变的单元格触发事件,关闭事件将加速VBA代码的执行。
ActiveSheet.DisplayPageBreaks:当在较新版本的Excel中运行VBA时,则可能比在早期版本的Excel中需要更长的时间完成。例如,需要几秒钟在早期版本的Excel中完成的宏可能需要几分钟才能在更高版本的Excel中完成。或者,第二次运行一个宏可能比第一次运行需要的时间更长。这是由于VBA宏修改了多行或列的属性,或者必须强制执行计算Excel分页符。如果宏设置了任何PageSetup属性或者手动设置了PageSetup属性,接着运行较大区域的行或列属性设置时会出现这样的问题。您可以将该属性设置为False来提高代码的运行速度。当然,在代码运行结束前,应将该属性恢复为原设置。

在单个操作中读/写大块的单元格区域
本技巧用于优化在Excel和代码之间转换数据的次数。使用数组变量存储所需要的值并执行取值或赋值操作,而不是一次遍历单个单元格并获取或设置单个值。
例如,下面的代码在单元格区域A1:C10000中放置随机数。
代码段一:运行速度较慢的代码

Sub testSlow()
Dim DataRange As Range
Dim Irow As Long
Dim Icol As Integer
Dim MyVar As Double
Set DataRange = Range("A1:C10000")
 
For Irow = 1 To 10000
For Icol = 1 To 3
MyVar = DataRange(Irow, Icol) '从Excel单元格中读取值30K次
If MyVar > 0 Then
MyVar = MyVar * MyVar ' 改变值
DataRange(Irow, Icol) = MyVar '将值写入Excel单元格中30000次
End If
Next Icol
Next Irow
End Sub

代码段二:运行速度更快的代码

Sub testFast()
Dim DataRange As Variant
Dim Irow As Long
Dim Icol As Integer
Dim MyVar As Double
DataRange = Range("A1:C10000").Value '一次从Excel单元格中读取所有的值,将其放入数组

For Irow = 1 To 10000
For Icol = 1 To 3
MyVar = DataRange(Irow, Icol)
If MyVar > 0 Then
MyVar = MyVar * MyVar ' 改变数组中的值
DataRange(Irow, Icol) = MyVar
End If
Next Icol
Next Irow
Range("A1:C10000").Value = DataRange '一次将所有结果写回单元格
End Sub

避免选取/激活对象
使用选取的方法更新单元格区域是最慢的。在试验了使用Range对象、使用Variant类型和使用Select方法对一个大的单元格区域读写数据的操作后,Select方法是最慢的。
再来看一个例子:在工作表中有40个形状,在每个形状中写入“Hello”。使用Select方法的代码为:

Sub testSlow()
Dim i As Integer
For i = 0 To ActiveSheet.Shapes.Count
ActiveSheet.Shapes(i).Select
Selection.Text = "Hello"
Next i
End Sub

运行速度更快的方法是完全避免使用选取并直接引用形状:

Sub testFast()
Dim i As Integer
For i = 0 To ActiveSheet.Shapes.Count
ActiveSheet.Shapes(i).TextEffect.Text = "Hello"
Next i
End Sub

在使用宏录制器时,所生成的程序代码在应用任何方法或属性之前都会激活或者选择对象。但是,并不是在所有的情况下都需要这样做。所以,在您编写VBA程序代码时,不需要在对对象执行任何任务之前都激活或者选择每个对象。
例如,在Excel中,我们如果要使第一行变成粗体就必须先选项中它。但在VBA中(除在图表操作时需要选中图表对象外),很少需要这样做,即VBA可以在不选中第一行的情况下,将它变成粗体。
宏录制器的代码:

Rows("1:1").Select
Selection.Font.Bold = True

改编后的代码为:

Row(“1:1”).Font.Bold=True

这样做还可以使程序代码更简洁,并且程序可以运行得更快。

工作簿设计
好的工作簿设计和数据组织有助于编写运行良好的代码。良好设计的工作簿,其执行效率和维护量将大大优化。可以说,工作簿设计是从大的宏观方面进行优化,而对代码的优化只是一些微观的细节上的优化。

其他

  • 尽量简化代码
    通过简化代码,可以提高程序的性能。您可以将通用过程编写为子过程来调用。例如,假设有一个应用程序需要在不同的地方实现查找一定范围内的某个特殊条目,在一个没有简化代码的应用程序中,不同的过程可能需要应用各自的算法以实现在某个范围内查找某一条目,修改每个过程使其采用一个更有效的算法并不是一件很容易的事。而一个简化的程序则只有一个查找算法,即将该查找算法编写成通用的子程序,需要查找某个范围的过程都调用该子程序,通过在查找方法的子程序中优化查找算法,使得调用该方法的所有过程都享受性能提高所带来的好处。
    另外,删除所有无关的代码,这在所录制宏中表现得尤为明显。在录制宏时,经常会产生一些与所实现的功能无关的代码,您可以将这些代码删除,以使得代码得以简化。
    宏录制器生成无效代码的一个原因是它不知道在对话框中您选择了哪些选项,因此,当您关闭对话框时它将直接记录所有可用的选项。例如,选择单元格区域G2:G20,然后在单元格格式对话框中改变字体样式为粗体,使用宏录制器生成的代码如下:

    Sub NowThis1()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为了进行测试,将循环100次
    Dim N As Long
    For N = 1 To 100
    '***************************
    Range("G2:G20").Select
    With Selection.Font
    .Name = "Arial"
    .FontStyle = "Bold"
    .Size = 10
    .Strikethrough = False
    .Superscript = False
    .Subscript = False
    .OutlineFont = False
    .Shadow = False
    .Underline = xlNone
    .ColorIndex = xlAutomatic
    End With
    '***************************
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    您能只用下面的一行代码为指定的单元格设置字体样式,不需要选择单元格区域。

    Range("G2:G20").Font.FontStyle = "Bold"

    如果您考虑到您想要宏所做的事情(本例中为使字体加粗),那么您可以查阅应用到Font对象的属性和方法列表,您将知道只需使用Bold属性编写这个宏代码以实现所需的功能。代码如下:

    Sub NowThis2()
    '快约10倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为进行测试,将循环100次
    Dim N As Long
    For N = 1 To 100
    '***************************
    Range("G2:G20").Font.Bold = True
    '***************************
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间为" & Finish - Start
    End Sub

    您也能在用户界面中通过执行不同的方法来录制产生结果相同的操作对宏录制器进行试验。例如,如果您通过标准工具栏上的粗体按钮格式化某区域为粗体,那么宏录制器将使用Bold属性。
    下面将要讲到的设置对象变量代替长对象引用,使用With…End With语句、执行For Each…Next循环语句,根据程序环境尽量减少OLE引用,等等,均是简化代码的好方法。

  • 强制声明变量
    在VBE编辑器中的菜单“工具——选项”对话框中“编辑器”选项卡中,您应该始终保持“要求变量声明”复选框被选中,这样将在模块代码顶部出现Option Explicit语句,要求您在编写代码时对所有出现的变量均进行声明,这样,在使用变量时减少内存需求并加速性能。
    (1)要节省内存资源,必须始终用特定的数据类型声明所有变量。如果不使用特定的数据类型声明变量,VBA会创建Variant类型的变量,这将比任何其他数据类型要求更多的内存。
    (2)清楚每种数据类型需要多少内存以及它可以存储的值的范围。除使用较小的数据类型会导致隐性转换的情况外,应始终使用尽可能小的数据类型。例如,因为Integer类型的变量将被转换成Long类型的变量,应该将那些存储整型值的变量声明为Long类型,而不是Integer类型。
    (3)除非确实需要,应避免使用浮点数据类型。尽管Currency数据类型更大,但它比 Single 数据类型快,因为Currency数据类型不使用浮点处理器。
    (4)如果在一个过程中多次引用一个对象,可以创建对象变量,并将对给对象的引用指派给它。因为对象变量存储对象在内存中的位置,VBA将不必再次查找其位置。
    (5)将对象变量声明为特定的类型(不是Object类型),以便利用早期绑定。
    (6) 减少”Variant”类型变量的使用
    虽然您可能发现在您的代码中使用Variant(变体)变量是方便的,但是如果您将变量清楚地声明为特定的数据类型,然后用VBA处理存储在该变量中的值,要比处理存储在Variant变量里的值快。
    如果执行不涉及分数值的数学运算,那么在您的代码中使用Long型变量比使用Variant变量更快。Long型变量也是在For…Next循环中索引值变量类型的最好选择。
    然而,您要注意到,您使用特定类型变量所获取的速度是以失去灵活性为代价的。例如,当使用特定数据类型时,您可能遭到变量溢出或类型不匹配的情形,而不会像Variant变量会自动进行类型转换处理。
    (7) 声明时指定特定的对象类型
    当您的宏被编译或者是运行(后台编译)时,会解析对象及它们的方法和属性的引用。经过宏编译解析的引用比在程序运行时必须被解析的引用要更快,因此,您最好跳过后台编译。
    如果您声明变量和参数为特定的对象类型(比如Range或Worksheet),VBA在编译您的程序时将解析引用为这些对象的属性和方法。(如果要查找指定对象类型列表,请参见”对象浏览器”)
  • 减少变量的作用范围并及时释放变量
    主要是对象变量,在其使用完后,及时释放。例如,

    Dim TempObj As AnyObject,AnObj As AnyObject
    Set TempObj=New AnyObject
    Set AnObj=TempObj
    Set TempObj=Nothing ‘释放对象变量
  • 使用常量
    变量会发生变化,因此VBA在程序运行时必须获取当前变量的值。
    在应用程序中使用常量会使程序运行更快。在编译您的代码时,常量仅计算一次并被存储。
    常量也能使您的宏程序更易阅读和维护。如果在您的程序中有一些不变的字符串或数值的话,您可以声明它们作为常量。
  • 尽可能使用早期绑定
    绑定是指将程序调用与实际代码相匹配。为了实现早期绑定,先应创建对对象库的引用。早期绑定可以在代码中使用定义在对象库中的常量,可以自动列出对象的方法和属性,但早期绑定只有在所控制的对象拥有独立的类型库或对象库文件才适用且还需要已安装了特定的库。而后期绑定则只是在运行时才知道对象的类型并对对象进行引用,因此不具备上述特点。
    使用早期绑定创建对象通常更有效率,使代码能获得更好的性能。因为对象的早期绑定引用在编译时可以通过VBE的解析,而不是通过运行时模块解析,因此早期绑定的性能要好得多。虽然在程序设计时不可能总是使用早期绑定,但应该尽可能使用它。
  • 使用For Each…Next循环
    可以使用For Each…Next循环来保证程序代码更快地执行。在使用For Each…Next循环时,对于存储在集合或数组中的每个对象执行一组语句,程序更简洁,也更容易阅读、调试和维护。当For Each…Next语句迭代集合时,自动指定一个对集合当前成员的引用,然后在到达集合的尾部时跳出循环语句。
    与使用计数进行循环相比,在遍历集合或数组时使用For Each…Next循环将更快。在多数情况下,使用For Each…Next循环也更方便,并且使您的宏更简洁、更容易阅读和调试。
    下面的示例运行很慢,因为在每次循环重复时它设置并调用了行变量.Row(i)。

    Sub DoSomethingSlow()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim Cell As Range, i As Long
    With Sheet1.Range("A1:A10000")
    For i = 1 To 10000
    Set Cell = .Rows(i)
    If Cell > 0 Then
    Cell.Font.ColorIndex = 5
    End If
    Next
    End With
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    下面的示例代码更简洁,其运行速度大约是上面代码的2~3倍。因为For Each…Next循环自动记录行数并定位,而不需要调用变量i。

    Sub DoSomethingFaster()
    '快两至三倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim Cell As Range
    With Sheet1
    For Each Cell In .Range("A1:A10000")
    If Cell > 0 Then
    Cell.Font.ColorIndex = 5
    End If
    Next
    End With
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub
  • 在执行循环时考虑如何能够尽可能地节省资源
    (1)分析循环以查看是否正在不必要地执行一些消耗内存的重复操作。例如,是否可以在循环外(而不是在循环中)设置某些变量?每次都通过循环执行的转换过程是否可以在循环之外执行?
    (2)考虑是否必须在满足特定的条件时才执行循环。如果是,也许可以更早地退出循环。例如,假设正在对一个不应该包含数字字符的字符串进行数据验证。如果循环要检查字符串中的每个字符以确定其中是否包含数字字符,那么您可以在找到第一个数字字符时立即退出循环。
    (3)如果必须在循环中引用数组的元素,可以创建一个临时变量存储该元素的值,而不是引用数组中的值。从数组中检索值比从相同类型的变量读取值要慢。
    (4) 将属性和方法放在循环外部
    在代码运行时,获取变量的值快于获取属性的值。因此,如果您的代码在循环内部获取属性的值,您可以在循环外部将该属性的值先指定给一个变量,然后在循环内部使用此变量代替属性的值,这样的代码将运行得更快。
    下面所示的代码运行较慢,因为在每次重复循环时都必须获取Sheet的Range属性的值。

    Sub TryThisSlow()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim MyLoop As Long
    For MyLoop = 2 To 4001
    Cells(MyLoop, 2) = Sheet1.Range("B1")
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    下面的示例与上面所产生的结果相同,但比上面的要更快,因为在循环开始以前我们已经将Sheet的Range属性的值指定给了单独的变量MyVar。这样,代码将在每次重复循环时利用该变量的值,而不必每次都要调有属性。

    Sub TryThisFaster()
    '快约35%以上
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim MyVar As String, MyLoop As Long
    MyVar = Sheet1.Range("B1")
    For MyLoop = 2 To 4001
    Cells(MyLoop, 2) = MyVar
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    如果您在一个循环内部使用多个对象访问,您也可以使用With…End With将您能够移动的对象移到循环外部。下面的示例在每次循环重复时都调用Sheets对象和Cells属性。

    Sub NowTryThisSlow()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim c As Long
    For c = 1 To 8000
    Sheet1.Cells(c, 5) = c
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    对上面的代码改写如下,使用With语句将调用Sheets对象移到循环外部,只剩余调用Cells。

    Sub NowTryThisFaster()
    '约快3倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim c As Long
    With Sheet1
    For c = 1 To 8000
    .Cells(c, 5) = c
    Next
    End With
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行时间为" & Finish - Start
    End Sub

    注:您也能通过使用对象变量在循环外部调用该对象。

  • 使用With…End With语句
    可以使用With…End With语句来尽量减少对象引用。使用With语句对指定的对象完成一系列的任务,而不用重复引用对象。也可以使用嵌套的With语句进一步提高程序代码的效率。例如,下面的使用With…End With语句是在同一个单元格中执行多个操作。

    With Workbooks("Book1.xls").Worksheets("Sheet1").Range("A1")
    .Formula="=SQRT(20)"
    With .Font
    .Name="Arial"
    .Bold=True
    .Size=10
    End With
    End With

    同理,可使用With…End With语句在同一个单元格区域中执行多个操作。

  • 尽量减少OLE引用
    调用每个VBA方法或属性都需要一个或多个OLE引用,这样在代码中会有多个点运算符,而每次代码调用都需要对这些点运算符进行解析,这将花费更多的时间。因此,在调用方法或属性时减少引用长度将是使您的程序运行更快的一种好方法。
    可以通过尽量减少在VBA程序代码中使用OLE(对象链接与嵌入自动识别)引用来优化程序代码。VBA语句中所调用的方法和属性越多,执行语句所用的时间就越多。例如下面的两个语句:
    语句1:

    Workbooks(1).Sheets(1).Range("A1").value="10"

    语句2:

    ActiveWindow.Left=200

    执行时,语句2比语句1快。
    同样,上面所讲的对重复使用的对象引用指定一个变量,通过调用变量从而保证避免多次进行对象引用。

  • 尽可能少使用“.”,使用对象变量
    在前面已经介绍过的对长对象引用使用对象变量以及使用With…End With等都是简化”.”的方法。因为在代码中的每个句点都表示至少一个(而且可能是多个)过程调用,而这些过程调用必须在后台执行。真正好的做法是在局部进行缓存对象引用,例如,应该把对象模型中较高层次的对象引用保存到局部对象变量中,然后用这些对象引用创建其他较低层次的对象引用。例如,引用某单元格数据时,可用如下代码:

    Dim i As Long
    For i=1 to 10
    Workbooks("Book1.xls").Worksheets("Sheet1").Cells(1,i).Value=i
    Next i

    但下面的代码运行效率更高,因为代码中引用Workbook对象和Worksheet对象的调用命令只执行一次,而上面的代码中却要执行10次。

    Dim ws As Worksheet
    Dim i As Long
    Set ws= Workbooks("Book1.xls").Worksheets("Sheet1")
    For i=1 to 10
    ws.Cells(1,i).Value=i
    Next i

    当您一遍又一遍的使用相同对象引用时,您可以将该对象引用设置成一个变量,然后使用该变量代替对象引用。这样,您在代码中只需对该对象变量进行引用即可。
    例如,下面的示例在每行中调用Workbook对象的Sheets属性、Range属性和Value属性三次,当您循环1000次时,总共要调用属性6000次。

    Sub DoThis1()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim N As Long
    For N = 1 To 1000
    Workbooks("Book1").Sheets(1).Range("c5").Value = 10
    Workbooks("Book1").Sheets(1).Range("d10").Value = 12
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    您能在循环开始前通过设置Workbooks(“Book1”).Sheets(1)作为一个对象变量来优化上面的例子,下面的示例在每行仅调用一个Range属性,当循环1000次时,总共只调用该属性2000次。
    注意,“Value”是一个缺省属性,通常不需要明确指定它,它将被自动调用。因此,该属性在下面的代码中被忽略。然而,就养成良好的编程习惯而言,还是建议您最好写明该属性。

    Sub DoThis2()
    '快约35%以上
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim ThisBookSheet As Object, N As Long
    Set ThisBookSheet = Workbooks("Book1").Sheets(1)
    For N = 1 To 1000
    ThisBookSheet.Range("c5") = 10
    ThisBookSheet.Range("d10") = 12
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    您可以比较这两个示例的运行速度,它们都得到同样的结果,但在我的机子上运行时,第二个示例比第一个快60%。当然,您还能使用With…End With语句获得相同的结果。
    您也能不设置明确的对象变量,而是使用With语句减少对象的重复引用。上面的示例也能使用下面的代码,该代码仅调用Workbooks属性和Sheets属性一次,当循环1000次时,总共调用1000次属性。

    Sub DoThis3()
    '快约35%以上
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim N As Long
    With Workbooks("Book1").Sheets(1)
    For N = 1 To 1000
    .Range("c5") = 10
    .Range("d10") = 12
    Next
    End With
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    上述三个示例均得到相同的结果,但在我的机子上运行时,本示例比第一个示例快50%以上。

  • 在一个语句中进行复制或者粘贴
    在用宏录制代码时,首先是选择一个区域,然后再执行ActiveSheet.Paste。在使用Copy方法时,可以在一个语句中指定复制的内容及要复制到的目的地。
    例如,将B5:C6区域的内容复制到以单元格B8开始的区域中,使用宏录制器的代码为:

    Range("B5:C6").Select
    Selection.Copy
    Range("B8").Select
    ActiveSheet.Paste

    经修改后的最佳代码是:

    Range("B5:C6").Copy Destination:=Range("B8")
  • 合理地使用消息框和窗体
    在一个很长的程序中,尝试着将消息框或者窗体安排显示在程序的最开始或最后面,避免干扰用户。此外,尽管窗体提供了许多功能,但它们能够导致文件大小迅速增加。还有就是尽量避免给工作表单元格链接用户窗体控件,因为这样将会导致链接更新操作,影响程序运行速度。
  • 尽可能加速对数字的运算
    (1)当对整数进行除法时,您可以使用整型除法运算符(\)而不是浮点除法运算符(/),因为无论参与除法运算的数值类型如何,浮点除法运算符总会返回Double类型的值。
    (2)在任何具有整数值的算术表达式中使用Single或Double值时,整数均将被转换成Single或Double值,最后的结果将是Single或Double值。如果要对作为算术运算结果的数字执行多次操作,可能需要明确地将该数字转换为较小的数据类型。
  • 提高字符串操作的性能
    (1)尽可能少使用连接操作。可以在等号左边使用Mid函数替换字符串中的字符,而不是将它们连接在一起。使用 Mid 函数的缺点是替换字符串必须与要替换的子字符串的长度相同。例如,

    Dim strText As String
    strText = "this is a test"
    Mid(strText, 11, 4) = "tent"

    (2)VBA提供许多可用来替换函数调用的内部字符串常量。例如,可以使用vbCrLf常量来表示字符串中的回车/换行组合,而不是使用Chr(13) & Chr(10)。
    (3)字符串比较操作的执行速度很慢。有时,可以通过将字符串中的字符转换为 ANSI 值来避免这些操作。例如,下列代码会检查字符串中的第一个字符是否为空格:

    If Asc(strText) = 32 Then

    上面的代码会比以下代码更快:

    If Left(strText, 1) = " " Then
  • 使用Asc()检验ANSI的值
    在VBA中,可以使用Chr$()函数把数转换成字符,并确定ANSI的值,但是更好的是使用Asc()函数把字符串转换成数值,然后确定它的ANSI值。如果需要进行有限次数的这种检验,对程序代码的效率可能不会产生很大影响,但是,如果需要在多个循环内进行这种检验时,这将节省处理时间并且有助于程序代码更快地执行。
  • 使用Len()检验空串
    尽管有多种方法可检验空串,但首选的是使用Len()函数。为了测试零长度的串,可以选择把串与””相比较,或者比较串的长度是否为0,但这些方法比用Len()函数要用更多的执行时间。当对字符串应用Len()函数并且函数返回0值时,说明该字符串是空的或者是零长度的字符串。
    并且,因为在If语句内非零值被认为是True,所以直接使用Len()函数而不必与””或0比较,减少了处理时间,因此执行更快。
  • 有效地使用数组
    用VBA数组而不是单元格区域来处理数据,即可以先将数据写入到某个数组,然后用一个语句就可以将数组中的数据传递到单元格区域中。(前文已述)
    在创建已知元素的确定数组时,使用Array函数对于节约空间和时间以及写出更具效率的代码是非常理想的。例如,

    Dim Names As Variant
    Names=Array("Fan","Yang","Wu","Shen")

    此外,应该尽量使用固定大小的数组。如果确实选择使用了动态数组,应该避免数组每增加一个元素就改变一次数组的大小,最好是每次增加一定数量的元素。

  • 使用Excel的内置函数
    对于要实现的某一功能,如果有Excel的内置函数能够实现,那么就用Excel的内置函数,不需要另外自定义函数,因为自定义的函数总比Excel内置的函数慢。
    考虑在VBA代码中使用工作表函数
    操作单元格区域的Excel工作表函数通常比完成同样任务的VBA程序更快(但不能确保总是这样,您可以对它们进行速度测试)
    例如,在代码中使用SUM工作表函数比用VBA代码在单元格区域中循环并相加值要快得多,以此为例,下面的代码运行速度相对较慢。

    Sub AddItSlow()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为了进行测试,我们循环5次
    Dim N As Long
    For N = 1 To 5
    '***************************
    Dim Cell As Range
    For Each Cell In Worksheets(2).Range("A1:G200")
    [a1] = [a1] + Cell.Value
    Next Cell
    '***************************
    Next N
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    下面的代码实现相同的功能,但运行得更快(几乎瞬间完成)。

    Sub AddItFaster()
    '快近600倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为了进行测试,我们循环5次
    Dim N As Long
    For N = 1 To 5
    '***************************
    [a1] = Application.WorksheetFunction. _
    Sum(Worksheets(2).Range("A1:G200"))
    '***************************
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    产生统计结果的函数(例如PRODUCT、COUNT、COUNTA和COUNTIF)是代替运行速度更慢的VBA代码的很好的选择,并且,一些工作表函数(例如MATCH和LOOKUP)能够将单元格区域作为参数。
    不要认为工作表函数总是更快的
    如下例所示,在VBA中没有Max或Min函数,但Excel中有该函数。于是,您能编写出如下代码:

    Sub MaxIt1()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为了测试,我们循环10000次
    Dim N As Long
    For N = 1 To 10000
    '***************************
    [J1] = Application.Max([J2], [J3])
    '***************************
    Next N
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行时间是" & Finish - Start
    End Sub

    或者,您能在VBA中使用下面的方式实现相同的功能:

    Sub MaxIt2()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    '为了测试,我们循环10000次
    Dim N As Long
    For N = 1 To 10000
    '***************************
    If [J2] >= [J3] Then [J1] = [J2] Else [J1] = [J3]
    '***************************
    Next N
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    比较上面的两个程序,可能认为使用工作表函数会更快,但事实上用VBA代码可以获得几乎相同的速度。因此,在一些大的循环中,您可以对实现同样功能的工作表函数的VBA代码进行测试。一些内置的VBA函数事实上运行速度也是慢的,因此,在编写代码时,在不同方式之间进行速度测试总是值得的。
    但是,在代码中经常使用的简单的函数,就直接编写代码,而不是使用WorksheetFunction对象。

  • 使用Range.SpecialCells()来缩小需要处理的单元格数。
  • 只要有可能就使用集合索引值
    您能在集合中使用名称或者数字来指定某个单一的对象,但使用对象的索引值通常是更快的。如果您使用对象的名字,VBA必须解析名字成为索引值;但如果您使用索引值,就能避免这个额外的步骤。
    但另一方面,我们要注意到在集合中通过名称指定对象有很多优点。使用对象名称能使您的代码更容易阅读和调试。此外,通过名称指定一个对象比通过索引值更安全,因为当您的代码运行时该对象的索引值可能变化。
    例如,某菜单的索引值表示它在菜单栏中的位置,但是如果在菜单栏中添加了菜单或者删除了菜单,该菜单的索引值会变化。这样,您就不应该考虑代码的速度,而应保证代码运行可靠。您使用索引值加快代码速度之前,应该确保该索引值在代码运行过程中或使用应用程序时不会改变。
  • 使用完全受限制的对象引用
    使用完全受限制的对象引用消除了引用模糊并确保变量有明确的类型。
    一个完全受限制的对象引用包括了对象库名称,如下代码所示:

    Dim wb As Excel.Workbook

    如果您使用通用的对象数据类型声明变量和参数,在运行过程中VBA可能必须对它们的引用进行解析为(某对象的)属性和方法,这将导致速度变慢。
    一个通用对象数据类型示例如下:

    Dim wb As Workbook
  • 使用已有的VBA方法
    也有一些特定目的的VBA方法,它们提供在单元格区域执行特定操作的一种简单的方式。例如工作表函数,这些特定的方法比使用通常的VBA编码完成相同的任务要更快。最常用的是”Replace”方法和”Find”方法。
    Replace方法:
    下面的示例用了一种相当慢的方式代码改变单元格区域H1:H20000中每个单元格的值。

    Sub NowDoThis1()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim Cell As Range
    For Each Cell In Worksheets(1).Range("H1:H20000").Cells
    If Cell.Value = 4 Then Cell.Value = 4.5
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    下面的示例使用Replace方法进行同样的操作,但运行得更快。

    Sub NowDoThis2()
    '快约两倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Worksheets(1).Range("H1:H20000").Replace "4", "4.5"
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    Find方法:
    下面的代码使用一种相对较慢的方法在单元格区域I1:I5000中值为4的单元格内添加一个蓝色的椭圆。

    Sub FindItSlow()
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim Cell As Range
    For Each Cell In Worksheets(1).Range("I1:I5000").Cells
    If Cell.Value = 4 Then
    With Worksheets(1).Ovals.Add(Cell.Left, _
    Cell.Top, Cell.Width, _
    Cell.Height)
    .Interior.Pattern = xlNone
    .Border.ColorIndex = 5
    End With
    End If
    Next
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    下面的示例使用了Find方法和FindNext方法执行相同的任务,但运行速度更快。

    Sub FindItFaster()
    '快约25倍
    Dim Start As Double, Finish As Double
    Start = Timer
    '--------------------------------------
    Dim Cell As Range, FirstAddress As String
    With Worksheets(1).Range("I1:I5000")
    Set Cell = .Find(4)
    If Not Cell Is Nothing Then
    FirstAddress = Cell.Address
    Do
    With Worksheets(1).Ovals.Add(Cell.Left, _
    Cell.Top, Cell.Width, _
    Cell.Height)
    .Interior.Pattern = xlNone
    .Border.ColorIndex = 5
    End With
    Set Cell = .FindNext(Cell)
    Loop Until Cell Is Nothing Or Cell.Address = FirstAddress
    End If
    End With
    '--------------------------------------
    Finish = Timer
    MsgBox "本次运行的时间是" & Finish - Start
    End Sub

    关于带有特定目的的VBA方法的更多的信息,您可参见VBA帮助系统相关主题。

结语
当然,代码优化可能不是绝对必要的,这依赖于您要做的工作……如果您正好编写了一个快速且简短的或者是一次性使用且与速度和/或简洁要求无关的代码,您就不需要优化代码。
但另一方面,如果您处理一个带有很多数据、工作簿、工作表等大的工程,再次检查您第一次编写好的代码,看看是否您的代码需要优化,而这样做总是值得的。
最终,您将养成编写代码的好习惯,将会使您的代码更简洁、运行更快速、并且容易为您自已和他人阅读和调试。同时,由于您的代码简洁,因而输入更快,工作效率更高。

参考资源

VBA + Excel Performance: Your Feedback Requested…

Excel VBA Performance Coding Best Practices

Excel 中显示分页符时,宏性能降低

Writing To A Range Using VBA

What is the fastest way to scan a large range in Excel?

Shapes and VBA code in Excel 2007

Improving Performance in Excel 2007

随机日志

Page 43 of 44« First...4041424344