VBA 应 用
  

  
  
  
  本章通过以下几个案例介绍VBA应用技术:国标汉字的输入和代码获取,文本查找,表格操作,图文框应用,状态栏控制,网页信息处理等。
5.1  快速输入国标汉字
  我国于1981年颁布了信息交换用汉字编码基本字符集的国家标准,即GB2312,对6763个汉字、628个图形字符进行了统一编码,为信息处理和交换奠定了基础。虽然Office 2016中文版支持超大字符集,但在多数情况下,人们用计算机处理的汉字一般都没有超出基本集这6763个汉字。
  由于某种特殊应用(如打印字帖、打印区位码表等),需要在Word文档中输入GB2312的全部汉字。一个个从键盘输入既慢又容易出错,显然不是好办法。编写一个VBA程序,可以轻松地解决这个问题。
  具体做法如下:
  进入Word,在“开发工具”选项卡“代码”选项组中单击“宏”按钮,在“宏”对话框中输入宏名“输入国标汉字”,指定宏的位置为当前文档,单击“创建”按钮,进入VB编辑环境,创建以下程序:

  Sub 输入国标汉字()
     For m = 176 To 247
        For n = 161 To 254
           nm = "&H" & Hex(m) & Hex(n)
           Selection.TypeText Text:=Chr(nm)
        Next
     Next
  End Sub

  这是一个双重循环结构程序,外层循环得到汉字内码的高位(范围是176到247之间的整数),内层循环得到汉字内码的低位(范围是161到254之间的整数)。循环体中,将内码的高位和低位以十六进制数字符形式拼接,即得到一个汉字的完整内码,用Chr函数将内码转换为汉字,用Selection.TypeText方法输入到当前文档。
  运行上述程序,便可在当前文档中得到GB2312的全部汉字。



5.2  查汉字区位码
  为保证汉字信息输入的准确性,有些场合要使用汉字的区位码。例如,填报中考、高考志愿表时,关键内容的汉字信息需要同时填写对应的区位码。通常,区位码可以查表得到,但是如果手头暂时没有区位码表,怎么查找每个汉字的区位码呢?下面的VBA程序可以解决这个问题。
  进入Word,在“开发工具”选项卡“代码”选项组中单击“宏”按钮,在“宏”对话框中输入宏名“查汉字区位码”,指定宏的位置为当前文档,单击“创建”按钮,进入VB编辑环境,创建以下程序:

  Sub 查汉字区位码()
     nm = Hex(Asc(Selection.Text))   	'内码(4位十六进制形式)
     nm_h = "&H" & Left(nm, 2)       	'内码(高2位)
     nm_l = "&H" & Right(nm, 2)      	'内码(低2位)
     qm = nm_h - 176 + 16            	'得到区码
     wm = nm_l - 161 + 1             	'得到位码
     wm = IIf(wm < 10, "0" & wm, wm)	'2位数表示
     MsgBox qm & wm                  	'显示区位码
  End Sub

  上述程序段首先取出选定文本(单个汉字),用ASC函数求出汉字的内码,用Hex函数将汉字的内码转换为4位十六进制字符串型数据。然后用Left和Right函数分别取出内码的高2位和低2位(用十六进制字符串表示)。最后将内码的高2位和低2位分别转换为区码和位码并显示出来。
  由于位码可能是1位数,也可能是2位数,因此为使格式规整,可用IIf函数统一转换为2位数。IIf函数可以根据条件的真假,返回不同的值,语法形式为:

  IIf(条件表达式, 条件为True时的返回值, 条件为False时的返回值)

  当变量wm的值小于10时,函数IIf(wm < 10, "0" & wm, wm)返回一个在wm的值前面加上一个字符“0”而形成的字符串。当变量wm的值大于或等于10时,则返回wm自身的值。
  要查询某个汉字的区位码,可先在Word中选中这个汉字,然后运行“查汉字区位码”子程序,就可得到该汉字的区位码。例如,“博”字的区位码为1809,“达”字的区位码为2079。
5.3  免试生筛选
  某高校计算机系要对学生进行软件开发能力考核。规定:如果“计算机导论”“数据库应用”“C语言”“数据结构”“VB程序设计”“多媒体技术”“汇编语言”“计算机网络”这8门课程的考试成绩中,有2门以上(含2门)排在全年级前10%以内,则“软件开发能力”成绩记为A等,不必另行参加考核。
  假设有一个Word文档,其中有8个表格,每个表格的内容为一门课成绩排在全年级前10%以内的学生名单和相关信息。图5-1给出了其中2门课对应的表格结构和内容。


图5-1  排在全年级前10%的单科成绩信息
  为了列出符合免试条件的学生名单,需要把8个表格中出现过2次以上的学生姓名挑选出来。虽然用“查找”等操作可以完成这一任务,但效率不高。下面介绍一种用VBA程序提高效率的方法。
  1. 编写程序
  打开含有图5-1所示的8门课成绩信息的Word文档,进入VB编辑环境,在当前工程中插入一个模块。
  在模块中输入语句Dim stt As String,声明一个模块级变量。然后,建立以下3个子程序:

  Sub 查次数()
    stt = Selection.Text                 	'选中的文本
    Selection.Find.Text = stt            	'作为查找的内容
    Selection.HomeKey Unit:=wdStory      	'到文件头
    Selection.Find.Execute               	'进行查找
    While Selection.Find.Found()         	'找到,计数,继续
      k = k + 1
      Selection.Find.Execute
    Wend
    MsgBox "该文本出现 " & k & " 次"     	'显示次数
    Selection.Find.Wrap = wdFindContinue	'回绕查找(光标回原位)
    Selection.Find.Execute
  End Sub
  Sub 下一处()
    Selection.Find.Text = stt            	'上一个操作选中的文本
    Selection.Find.Wrap = wdFindContinue	'回绕查找
    Selection.Find.Execute
  End Sub
  Sub 删除()
    Selection.SelectRow                 	'选中表格当前行
    Selection.Rows.Delete               	'删除表格当前行
  End Sub

  “查次数”子程序用来在当前文档中查找并显示选定文本出现的次数,然后光标回到原处。“下一处”子程序用来将光标定位到选定文本下一次出现的位置。“删除”子程序用来删除表格当前行。
  2. 在快速访问工具栏中添加按钮
  为了便于操作,可在Word当前文档的快速访问工具栏中添加3个按钮,分别用来执行“查次数”“下一处”“删除”子程序。添加方法如下:
  (1)右击Word的快速访问工具栏,在快捷菜单中选择“自定义快速访问工具栏”命令,打开图5-2所示的“Word选项”对话框。在“从下列位置选择命令”下拉列表框中选择“宏”项,在“自定义快速访问工具栏”下拉列表中选择“用于‘免试生筛选.docm’”,将左侧列表框中的3个宏添加到右侧列表框。


图5-2 “Word选项”对话框
  (2)在右侧列表框中选中一个宏,单击列表框下面的“修改”按钮。在图5-3所示的“修改按钮”对话框中,指定按钮的图标符号、修改显示名称,然后单击“确定”按钮。

  用同样方法对另外2个宏进行修改,最后单击“Word选项”对话框的“确定”按钮,Word当前文档的快速访问工具栏中就会出现添加的这3个按钮。
  3. 名单筛选
  在文档中选定一个学号或姓名,单击快速访问工具栏上“查次数”按钮,显示它在文档中出现的次数。如果次数等于1,就说明该生不符合免试条件,可直接单击“删除”按钮进行删除。如果次数大于1,则说明该生符合免试条件,应该保留该生一条记录,删除其余的记录。方法是单击“下一处”和“删除”按钮,直至将多余的记录全部删除,再单击“下一处”按钮,将光标定位到原处,以便处理下一条信息。依次处理每条记录,最后保留的就是符合免试条件的学生名单。
5.4  名片制作模板
  尽管利用Word 2016的在线模板可以制作名片,但操作比较复杂,格式也不够灵活。用Word 2016的宏创建一个名片制作模板,有一定实用性。本节介绍这个模板的设计和使用方法。
  1. 页面及图文框设置
  创建一个Word文档,保存为启用宏的Word文档:“名片制作模板.docm”。
  右击Word的快速访问工具栏,在快捷菜单中选择“自定义快速访问工具栏”命令,打开“Word选项”对话框。在“从下列位置选择命令”下拉列表框中选择“不在功能区中的命令”项,在“自定义快速访问工具栏”下拉列表中选择“用于所有文档”,将左侧列表框中的“插入横排图文框”项添加到右侧列表框。单击“确定”按钮,在Word左上角的快速访问工具栏上即可看到“插入横排图文框”按钮。
  在“布局”选项卡的“页面设置”选项组中,单击对话框启动按钮。在“页面设置”对话框中,选定“纸张”选项卡,指定“宽度”和“高度”分别为19.5厘米和29.5厘米(标准名片纸张规格)。选定“页边距”选项卡,指定上、下、左、右边距均为0.7厘米。单击“确定”按钮,退出“页面设置”。
  单击快速访问工具栏的“插入横排图文框”按钮,在当前文档空白处拖动鼠标,添加一个图文框。选中图文框,右击,在快捷菜单中选择“设置图文框格式”命令。在图?5-4所示的“图文框”对话框中,设置宽度为固定值8.6厘米、高度为固定值5.4厘米(标准名片规格)。单击“确定”按钮,退出“图文框”设置。
  选中图文框,右击,在快捷菜单中选择“边框和底纹”命令。在“边框和底纹”对话框的“边框”选项卡中,设置“无”边框,单击“确定”按钮,将图文框的边框取消。


图5-4 “图文框”对话框
  2. 编写代码
  进入VB编辑环境,插入一个模块,编写一个“复制及排版”子程序,代码如下:

  Sub 复制及排版()
    Selection.Cut                                   		'剪切选中的图文框
    For k = 1 To 10                                 		'粘贴10次
      Selection.Paste
    Next
    Selection.MoveLeft Unit:=wdItem                		'选中第1个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameLeft           		'水平相对于页边距、左侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameBottom               	'垂直相对于页边距、底端
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
    Selection.MoveLeft Unit:=wdItem                   	'选中第2个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameRight              	'水平相对于页边距、右侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameBottom               	'垂直相对于页边距、底端
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
    Selection.MoveLeft Unit:=wdItem                   	'选中第3个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameRight              	'水平相对于页边距、右侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = CentimetersToPoints(17.7) '垂直相对于页面、17.7厘米
      .RelativeVerticalPosition = wdRelativeVerticalPositionPage
    End With
    Selection.MoveLeft Unit:=wdItem                  	'选中第4个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameLeft              	'水平相对于页边距、左侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = CentimetersToPoints(17.7) '垂直相对于页面、17.7厘米
      .RelativeVerticalPosition = wdRelativeVerticalPositionPage
    End With
    Selection.MoveLeft Unit:=wdItem                   	'选中第5个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameLeft               	'水平相对于页边距、左侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameCenter               	'垂直相对于页边距、居中
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
    Selection.MoveLeft Unit:=wdItem                  	'选中第6个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameRight              	'水平相对于页边距、右侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameCenter               	'垂直相对于页边距、居中
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
    Selection.MoveLeft Unit:=wdItem                   	'选中第7个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameRight              	'水平相对于页边距、右侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = CentimetersToPoints(6.4)  	'垂直相对于页面、6.4厘米
      .RelativeVerticalPosition = wdRelativeVerticalPositionPage
    End With
    Selection.MoveLeft Unit:=wdItem                 	'选中第8个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameLeft              	'水平相对于页边距、左侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = CentimetersToPoints(6.4)  	'垂直相对于页面、6.4厘米
      .RelativeVerticalPosition = wdRelativeVerticalPositionPage
    End With
    Selection.MoveLeft Unit:=wdItem                  	'选中第9个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameRight             	'水平相对于页边距、右侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameTop                 	'垂直相对于页边距、顶端
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
    Selection.MoveLeft Unit:=wdItem                  	'选中第10个图文框
    With Selection.Frames(1)
      .HorizontalPosition = wdFrameLeft              	'水平相对于页边距、左侧
      .RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin
      .VerticalPosition = wdFrameTop                 	'垂直相对于页边距、顶端
      .RelativeVerticalPosition = wdRelativeVerticalPositionMargin
    End With
  End Sub

  在上述子程序中,首先把选中的图文框剪切下来,再粘贴10次,得到10个相同的图文框。然后,依次选中每个图文框,设置不同的水平和垂直位置,使这10个图文框均匀排列到整个页面。
  3. 使用方法
  用以上方法创建的“名片制作模板.docm”,可以作为一个应用软件,需要时随时打开,用它来设计、打印名片。具体使用方法如下:
  (1)打开“名片制作模板.docm”。
  (2)在图文框中输入名片的具体内容,设置字体字号,进行排版,可以插入图片和进行艺术加工,设计出一张名片样板。
  (3)选中名片样板图文框。
  (4)运行“复制及排版”子程序,Word便自动将设计好的名片样板复制10份并均匀地排列在整个页面上。
  (5)将整个页面内容打印输出到名片纸上。
  (6)用裁纸机将每页纸上的10张名片裁剪下来。
5.5  在状态栏中显示进度条
  利用Word状态栏,可以制作动态的进度条。将这一技术应用到软件当中,能够直观地显示工作进度,改善用户长时间等待的心理状态。
  创建一个Word文档,保存为“在状态栏中显示进度条.docm”。
  进入VB编辑环境,在当前工程中插入一个模块,在模块中编写一个“显示进度”子程序,代码如下:

  Sub 显示进度()
    wtm = "当前进度:"
    kk = "◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇"
    sk = "◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆"
    ck = Len(kk)                            		'进度条长度
    n = 500                                 		'循环次数
    m = 1 + n \ ck                          		'每循环m次,刷新进度条1次
    For k = 1 To n                          		'循环
      Selection.TypeText Text:=Rnd          	'模拟要执行的操作
      Selection.TypeParagraph
      If k Mod m = 0 Then                   		'k为m的整数倍
        c = k \ m                           		'进度格数量
        p = Left(sk, c) & Right(kk, ck - c) 	'调整进度格
        Application.StatusBar = wtm & p     	'更改系统状态栏的显示
      End If
      DoEvents                              		'转让控制权给操作系统
    Next
    Application.StatusBar = False           	'恢复系统状态栏
  End Sub

  上述子程序首先用变量wtm保存字符串“当前进度:”。定义两个变量kk和sk,分别保存由空心菱形块和实心菱形块组成的字符串,并求出字符串的长度ck。
  然后,用变量n表示循环次数,变量m表示经过多少次循环才刷新一次进度条,用For语句进行n次循环。
  每次循环除了模拟要执行的操作(在当前文档输出一个随机数并换行)外,还要判断循环变量k能否被m整除。若k能被m整除,即k为m的整数倍,则求出进度条应有的实心菱形块数量,从sk和kk字符串左右两边分别取出一定数量的字符,拼成新的字符串用p表示,并将p与变量wtm的值拼接后显示在系统的状态栏上。语句DoEvents转让控制权给操作系统,起到刷新屏幕作用。
  最后,恢复系统状态栏。
  运行“显示进度”子程序,会在当前Word文档输出模拟数据,并在状态栏上动态显示进度条,如图5-5所示。


图5-5  Word状态栏上的进度条
5.6  哥德巴赫猜想问题
  公元1742年,德国数学家哥德巴赫提出了著名的猜想:任何一个大于或等于6的偶数,都可以表示成两个素数之和。例如,6=3+3、8=3+5、10=5+5、…、100=3+97=11+89=17+83,等等。在这些具体的例子中,可以看出哥德巴赫猜想都是成立的。有人甚至逐一验证了3300万以内的所有偶数,竟然没有一个不符合哥德巴赫猜想的。随着计算机技术的发展,数学家们发现哥德巴赫猜想对于更大的数依然成立。可是自然数是无限的,谁知道会不会在某一个足够大的偶数上,突然出现哥德巴赫猜想的反例呢?因此用逐一验证的方法显然不可取。
  本节绝无证明哥德巴赫猜想之意,只是出于好奇,通过VBA程序,在一定范围内验证哥德巴赫猜想。目的是激发学习兴趣,提高程序设计技巧和应用能力。
  1.编写自定义函数
  判断一个数是否为素数,可以用一个自定义函数来实现。
  创建一个Word文档,保存为“哥德巴赫猜想问题.docm”。按Alt+F11快捷键,进入VB编辑环境。插入一个模块,编写一个自定义函数isprime,代码如下:

  Function isprime(n)
    For k = 2 To Sqr(n)
      If n Mod k = 0 Then
        isprime = False
        Exit Function
      End If
    Next
    isprime = True
  End Function

  上述自定义函数的功能是判断自变量n是否为素数。如果 n是素数,函数的返回值为True,否则,函数的返回值为False。方法是用2到Sqr(n)之间的所有整数去试除n,如果这些数都不能整除n,则n是素数,否则n不是素数。
  2.编写子程序
  在模块中编写一个“哥德巴赫猜想”子程序,代码如下:

  Sub 哥德巴赫猜想()
    wtm = "当前进度:"
    kk = "◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇"
    sk = "◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆"
    ck = Len(kk)                            		'进度条长度
    n = 500                                 		'循环次数
    m = 1 + n \ ck                          		'每循环m次,刷新进度条1次
    i = 2000                                		'偶数起始值
    For k = 1 To n                          		'循环
      j = 3
      Do
        If isprime(j) And isprime(i - j) Then
          Selection.EndKey Unit:=wdStory 		'光标定位到文档尾
          Selection.TypeText Text:=i & "=" & j & "+" & i - j & Chr(10)
          Exit Do
        End If