VBA进阶知识
第十五篇、VBA进阶:制图、窗体与插件、Office协同
VBA特别适合“办公自动化、数据处理和定制化解决方案。Excel超出内置函数能力范围的复杂数据操作、清洗、转换和分析的高级应用,都可以用VBA处理。
看到没,它的核心优势和强项在于处理数据,也就是操控工作簿、工作表、单元格的里内容,让它们在按照我们编程好的处理步骤,自动化重复性的去办公。
但是VBA能做的工作远不止此:
- 还记得我们在🔗第二章:了解宏的录制里提到的吗?录制宏代码,就是用代码记录你对表格的操作步骤,然后复现出来。相当于只要是你能Excel手动操作的拖、拉、拽,都有代码在记录。这就意味着,VBA制图也是非常可以的!
- 辛苦编好的程序解决了问题,当然希望它能被团队共享和使用。VBA 窗体(UserForms)能创建专业的交互界面,让其他成员按照使用的要求规范来完成,让用户像使用APP一样,单击按钮、输入条件、执行结果汇出等,不必在切换到Visual Basic编辑器找对应的模块和过程,毕竟不是每个人都懂VBA!
- VBA插件 (Add-ins)则是解决了 VBA 代码的分发、重用和集成问题,让开发的功能更容易被团队共享和使用。
- 至于Office协同,是 VBA不再局限于单个 Office 应用。让它可以和Word交互(比如将表格、计算结果自动填充到Word的指定位置、批量生成格式化的word文档等)、可以和PPT交互(比如将Excel中的图表、表格链接到PPT的幻灯片,保持数据更新和同步等)。VBA的Office协同能力可以视作“胶水语言”!
这三个工作听着是不是很酷炫?其实它们在实际工作中用的并不多!
- 汇报不是天天做,制图被拖拉拽后粘贴到PPT已足够了,在Excel直接操作更直观;
- 定制化的解决方案意味着它不是普适性的,只是解决当前的某个问题而开发的,共享代码几乎不存在;
- Office协同,我记得生涯中只用过一次和Word的交互,就是“条件格式只希望保留单元格的设置,不把设置条件同步他人,就会把一个带有条件格式的表格,复制出来粘贴到Word,然后在剪切回来后,条件格式设置的红黑底色还在但是条件清除掉了。”
将上述三项内容列为进阶知识范畴。对于想了解它们的人、分享内容肯定是不足的,有兴趣的请查阅专业文献资料,但愿它们能让你的思路开阔一些!如果对解决您的当前工作有帮助,也欢迎和我互动,此类案例在我实际工作中接触有限,多了解些它们能解决的现实工作案例最好不过!
本篇视频:
|01 制图
我们用Excel还经常做什么呢?---图表。
一、内置函数
相信每个人都有Excel数据制图的经历,柱状图、折线图、饼图......,几乎每次的汇报里都有它们的身影,我们通过在Excel表格中,选择数据→选择图形→插入图表→调整图表选项(标题、标签、坐标刻度、线条颜色等),就完成了一个数据图制作!当然这些也可以用VBA代码来完成!
在表格中插入图表制图是用鼠标来控制,而用代码制图,就像是一个画家作画的过程,得先有一个画板(ChartObject图表容器)、在有一张画纸(Chart图表)、然后就是花花草草的绘制过程(HasTitle-标题、Axes()-分类的X\Y轴、HasLenged-图例等元素)。
VBA代码制图的优势在于,它可以高度精细化过程和定制,几乎能控制图表中的每个元素的表现,还可以添加非标准元素(如汇总文本框、添加箭头、注释)。
关于图表的结构和释义,即图表的组成部分有哪些?什么样的数据内容适合用哪种图表来展示?相信经常使用它们的人都会有清晰概念,就不再赘述。🔗如何制作专业有效的图表
示例:
图表组成和VBA代码制图示例:
Sub VBA制图表()
'声明变量
Dim ws As Worksheet
Dim Chart_Rng As Range '声明一个单元格变量
Dim chObj As ChartObject '声明一个ChartObject对象变量,代表嵌入在工作表中的一个图表容器
Dim ch As Chart '声明一个Chart对象变量,代表图表本身
Set ws = ThisWorkbook.Sheets("制图") '设置工作表
Set Chart_Rng = ws.Range("A1:B11") '设置源数据范围,品牌在A列、销量在B列
Set chObj = ws.ChartObjects.Add(Left:=180, Width:=500, Top:=10, Height:=250) ' 在工作表上创建一个新的图表对象(画板),指定位置和大小
Set ch = chObj.Chart '获取内部的Chart对象(画纸)
'在Chart对象(画纸)上操作,就像画画
ch.ChartType = xlColumnClustered '图表类型为簇(cù)状柱形图
ch.SetSourceData Source:=Chart_Rng '给图表指定数据源
ch.HasTitle = True '图表标题状态为显示
ch.ChartTitle.text = "一月份销量" '图表标题文字
'ch.HasLegend = True '图例状态为显示
'ch.Legend.Position = xlLegendPositionBottom '图例显示位置在底部
ch.HasLegend = False '不显示图例
'ch.Axes(xlCategory).HasTitle = True '设置分类轴(x轴)标题状态为显示
'ch.Axes(xlCategory).AxisTitle.Text = "品牌" 'x轴标题文字
With ch.Axes(xlCategory).TickLabels.Font '设置轴标签字体设置
.Size = 10
.Bold = True
End With
ch.Axes(xlCategory).TickLabels.Orientation = 45 ' 45度角
' 设置Y轴(数值轴)的主要网格线 X轴(分类轴)的主要网格线的引用方法.Axes(xlCategory).MajorGridlines.Border
With ch.Axes(xlValue).MajorGridlines.Border
.Weight = 1 ' 粗细(磅)
.color = RGB(100, 0, 0) ' 红色
.LineStyle = xlDash ' 虚线(xlSolid=实线,xlDot=点线)
End With
'美化数据系列
Dim srs As Series
Dim i As Long
Debug.Print ch.SeriesCollection.Count '数据系列的个数,当前示例中的数据系列只有一个销量,就返回1
For Each srs In ch.SeriesCollection '循环遍历ch.SeriesCollection集合,当前只有一个数据系列,循环一次即可
Debug.Print srs.Name '数据系列的名称
Debug.Print srs.Points.Count '数据系列里数据的个数
For i = 1 To srs.Points.Count '遍历每个数据标签
Debug.Print srs.XValues(i) '打印标签
Debug.Print srs.Values(i) '打印值
If i Mod 2 = 0 Then '偶数行我们改变柱体颜色、标签等,这里用奇偶数举例,也可以按照标签、值大小单独设置
srs.Points(i).Format.Fill.ForeColor.RGB = RGB(255, 0, 0) '柱体颜色为红色
'srs.Points(i).ApplyDataLabels '添加数据标签,直接应用并显示数据标签
srs.Points(i).HasDataLabel = True '启用指定数据点的标签(但不自动显示内容,需额外设置)
srs.Points(i).DataLabel.text = "※" & srs.Values(i) '数据标签的文本设置
With srs.Points(i).DataLabel.Font '数据标签进行设置,颜色、大小、粗细
.color = RGB(255, 0, 0)
.Size = 10
.Bold = True
End With
srs.DataLabels.Position = xlLabelPositionOutsideEnd '数据标签在柱形末端外
End If
Next i
Next srs
'调整Y轴的数据标签
With ch.Axes(xlValue)
Dim Maxvalue As Long
Maxvalue = Application.WorksheetFunction.Max(ch.SeriesCollection(1).Values) '数据列最大值
'设置最小值和最大值
.MinimumScale = 0
.MaximumScale = Application.WorksheetFunction.Max(ch.SeriesCollection(1).Values) * 1.1 '最大值是数据列最大值的110%
' 设置主要刻度单位
.MajorUnit = (Round((Maxvalue * 1.1 / 5) / 500, 0) + 1) * 500 '将主要刻度分成5等分,刻度线显示500的倍数
' 设置刻度线类型
.MajorTickMark = xlInside
End With
'添加注释说明
Dim maxPoint As Point, minPoint As Point
Dim maxIndex As Long, minIndex As Long
Set srs = ch.SeriesCollection(1) '当前只有一个数据系列,直接指定它
For i = 1 To srs.Points.Count ' 查找最大值和最小值的位置
If srs.Values(i) = Maxvalue Then maxIndex = i
If srs.Values(i) = Application.WorksheetFunction.Min(ch.SeriesCollection(1).Values) Then minIndex = i
Next i
Set maxPoint = srs.Points(maxIndex)
Set minPoint = srs.Points(minIndex)
' 添加最大值注释,call调用其他过程
Call AddDPA(ch, maxPoint, "销量最高: " & Format(Maxvalue, "#,##0"), RGB(0, 150, 0))
' 添加最小值注释
Call AddDPA(ch, minPoint, "销量最低: " & Format(Application.WorksheetFunction.Min(ch.SeriesCollection(1).Values), "#,##0"), RGB(200, 0, 0))
End Sub
' 添加数据点注释的通用函数
Sub AddDPA(ch As Chart, pt As Point, text As String, color As Long)
Dim shp As Shape, arrow As Shape
Dim arrowStartX As Double, arrowStartY As Double
Dim arrowEndX As Double, arrowEndY As Double
' 计算数据点中心位置 ,left左,Top上,位置距离的大小是相对图的边框而言
Dim pointCenterX As Double, pointCenterY As Double
pointCenterX = pt.Left + pt.Width / 2
pointCenterY = pt.Top + pt.Height / 2
' 确定注释框位置
Dim boxTop As Double, boxLeft As Double
boxLeft = pointCenterX - 60
boxTop = pointCenterY - 70
' 添加注释框
Set shp = ch.Shapes.AddShape(msoShapeRoundedRectangle, boxLeft, boxTop, 90, 20) '90,20是注释框的长和宽
With shp '对注释框的修饰
.TextFrame2.TextRange.text = text '字体内容
.TextFrame2.TextRange.Font.Size = 10 '字体大小
.TextFrame2.TextRange.Font.Bold = True '字体粗细
.Fill.ForeColor.RGB = color '字体颜色
.Fill.Transparency = 0.3 ' 30% 透明 '底色透明度
.Line.Weight = 0 '边框线条的粗细
End With
' 添加箭头(从注释框指向数据点)
arrowStartX = boxLeft + 60
arrowStartY = boxTop + 40
arrowEndX = pointCenterX
arrowEndY = pointCenterY
Set arrow = ch.Shapes.AddLine(arrowStartX, arrowStartY, arrowEndX, arrowEndY) 'arrowStartX, arrowStartY起始位置的X/Y点,arrowEndX, arrowEndY是结束位置
With arrow.Line '对线条的修饰
.ForeColor.RGB = color
.Weight = 1.5
.EndArrowheadStyle = msoArrowheadTriangle
.EndArrowheadWidth = msoArrowheadWidthMedium
.EndArrowheadLength = msoArrowheadLengthMedium
End With
End Sub
- 在图中标记箭头、标签等非标准元素,它不是图表中的元素且非必须,我们可以把它写一个独立过程-AddDPA,有需要时直接call调用即可。
以上示例是一个完整的用VBA制作柱状图的过程(当然有些代码行是非必要的,之所以写上又注销掉、或者频繁debug.print打印,是为了展示图表元素的属性,更便于理解)。
回顾Range、Worksheet和Workbook对象(第6-9章开篇的“对象的层级结构图”),图表也是Excel的对象类型之一,它也有属于自己的属性和方法,同样可以通过控制它们的值和状态,达到制图的目的。
用VBA代码的制图过程看着挺复杂,没有直接在Excel表格中插入、选择元素、设置来得方便,但是它也有远超手动操作的强大能力,比如:它可以批量生成;它可以条件格式化图表;它可以精细化控制与定制;它可以添加非标准元素。
仍以以上的示例代码举例,当前只是1月的,如果是1-12月分月的都需要一张类似的图呢?
写一个循环,引用不同的数据区域,不就可以一键批量生成了吗? 它能按照数据标签间隔1个的条件改变颜色,当然也可以按照数据的增长、高低阙值来突出显示;它能精细控制数据轴标签的显示;还能直接在图中标记箭头、标签等需要关注的内容,一目了然!
常用图表类型:
变量声明、创建图表对象、指定数据源的方法都是一样的,把.ChartType = **** 指定为什么类型,就会创建该类型的图表。图表类型很多,有需要用到时可以查阅资料!
图表元素部分常用属性和方法:
- 图1:图表组成里的各元素,它们的父对象就是图表对象(.Chart);
- 所有能返回数组的对象元素,都可以循环遍历。
示例:窗体使用
窗体设计与开发,一个员工档案录入的示例:
窗体代码,数据验证、规范录入的行为。本文视频中有详细的窗体设计步骤。
'一个工作表事件,当前工作表被激活时,就显示(.show)"人员录入"的窗体
'前提是我们在开发时,把应插入到当前表的窗体命名为"人员录入"
Private Sub Worksheet_Activate()
人员录入.Show
End Sub
' 窗体初始化代码,就是窗体.show初始显示是的部分设置
Private Sub UserForm_Initialize()
' 设置学历下拉框选项
'me表示当前UserForm窗体,Edu_txt是窗体中的一个"控件"的名称
With Me.Edu_txt
.AddItem "高中"
.AddItem "大专"
.AddItem "本科"
.AddItem "硕士"
.AddItem "博士"
End With
' 设置默认在职状态
Me.OptAct.value = True 'OptAct是窗体中的一个"控件"的名称
End Sub
'btnSubmit是窗体中的一个"控件"的名称,_Click表示按下、单击
'该过程解释,当btnSubmit这个控件被鼠标单击单击时,它会什么?
Private Sub btnSubmit_Click()
' 验证姓名,不满足条件就程序退出过程,规范姓名的录入
If Trim(Me.Name_txt.value) = "" Then '不能为空
MsgBox "姓名不能为空!", vbExclamation, "输入错误"
Me.Name_txt.SetFocus '定位到姓名录入窗口
Exit Sub
ElseIf InStr(Me.Name_txt.value, " ") > 0 Then '不能包含空格
MsgBox "姓名不能包含空格!", vbExclamation, "输入错误"
Me.Name_txt.SetFocus
Exit Sub
End If
' 验证年龄,不满足条件就程序退出过程,规范年龄的录入
If Not IsNumeric(Me.Age_txt.value) Then '必须是数值
MsgBox "年龄必须是数字!", vbExclamation, "输入错误"
Me.Age_txt.SetFocus
Exit Sub
ElseIf Me.Age_txt.value < 18 Or Me.Age_txt.value > 65 Then '年龄是有范围的
MsgBox "年龄必须在18-65岁之间!", vbExclamation, "输入错误"
Me.Age_txt.SetFocus
Exit Sub
ElseIf InStr(Me.Age_txt.value, ".") > 0 Then '年龄是整数
MsgBox "年龄必须是整数!", vbExclamation, "输入错误"
Me.Age_txt.SetFocus
Exit Sub
End If
' 验证学历,它是一个下列框来选择,不能是空值和其它的选择框之外的内容
If Me.Edu_txt.value = "" Then
MsgBox "请选择学历!", vbExclamation, "输入错误"
Me.Edu_txt.SetFocus
Exit Sub
End If
' 验证在职状态,在职状态是单选,就是三个状态是排它的必选项
Dim status As String
If Me.OptAct.value Then
status = "在职"
ElseIf Me.OptRes.value Then
status = "离职"
ElseIf Me.OptPro.value Then
status = "试用"
Else
MsgBox "请选择在职状态!", vbExclamation, "输入错误"
Exit Sub
End If
' 添加数据到工作表,如果以上数据验证都,就写入到表格
Dim ws As Worksheet
Dim Row_end As Long
Set ws = ThisWorkbook.Sheets("窗体与插件")
Row_end = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1 '定位到数据表非空的末行的下一行
'表格的ABCD列,对应写入姓名、年龄、学历、在职状态等值
With ws
.Cells(Row_end, 1).value = Trim(Me.Name_txt.value) '添加姓名
.Cells(Row_end, 2).value = CInt(Me.Age_txt.value) '添加年龄
.Cells(Row_end, 3).value = Me.Edu_txt.value '添加学历
.Cells(Row_end, 4).value = status '添加状态
End With
’最后弹出“成功”的消息框反馈给用户,并清空窗体的文本框便于继续维护
MsgBox "员工信息添加成功!"
' 清空表单,恢复到初始状态,清空表单是一个单独的过程,调用即可!
Call btnClear_Click
End Sub
' 清空表单的过程代码,恢复到初始状态,它也是一个btnClear控件下的代码
Private Sub btnClear_Click()
Me.Name_txt.value = ""
Me.Age_txt.value = ""
Me.Edu_txt.value = ""
Me.OptAct.value = True '在职状态为默认
Me.Name_txt.SetFocus '姓名录入窗口为当前窗口
End Sub
' 关闭按钮代码,btnClose控件,单击后的代码
Private Sub btnClose_Click()
Unload Me '取消加载就是隐藏Me(当前)的这个窗体
End Sub
- 以上是一个简单的窗体的设计与开发示例,它演示了窗体内不同控件和控件背后的代码,像APP一样的前端的操作,这样对用户来说体验是不是更好?
示例:插件的使用
推荐内置VBA插件,使用.xlam格式创建可重用的加载项:
以2013版为例分步说明,本文视频中有关于插件使用的同步连贯视频
1、将开发的包含代码的文件另存为一个加载宏.xlam的文件,(示例命名“交互”)
2、在新的工作簿中:选择 开发工具 → Excel 加载项 → 浏览 → 选择“加载宏文档”,加载后 → 确定,载入(名为“交互”)的宏文件
3、文件 → 选项 → 自定义工具栏(可以找到加载的宏)→ 新建新选项卡 → 新建功能区和组后添加宏,将加载的宏移动到新的功能区下
4、在表格中出现新的工具栏和按钮,即可执行分享的代码
|03 Office协同
VBA通过COM(Component Object Model)技术实现Office应用间的协同。每个Office应用都有对象模型,Word是Document,Excel是Workbook。
示例:
通过一个Excel和Word交互的示例来认识它:从Excel数据表中把内容写入到Word,在从Word写入到的Excel数据表中的两个过程。
对学生的成绩评价,姓名增加底色,标准是成绩“40分以下是灰色,60-79之间是蓝色,高于80分是红色,其他不增加底色”,把这种对包含底色的成绩表同步他人,但是不希望他们知道评价的标准。
Sub office协同Excel_Word()
'自定义数据类型
Dim Choose_rng As Range
Dim wdap
On Error Resume Next
'选择需要设置的单元格区域,把单元格区域设置为可选择,普适性会更好
Set Choose_rng = Application.InputBox("选择需要设置的单元格或单元格区域", Type:=8)
If WorksheetFunction.CountA(Choose_rng) = 0 Then MsgBox "选择单元格区域为空": Exit Sub
On Error GoTo 0
Application.ScreenUpdating = False '关闭刷新
Application.DisplayAlerts = False '关闭警告
Set wdap = CreateObject("Word.Application") ' 创建 Word 应用
With wdap
.Visible = True
.Documents.Add '添加一个word表
End With
Choose_rng.Copy '复制表黏贴到Word文档
wdap.Selection.Paste
wdap.Selection.WholeStory '复制word文档的内容
wdap.Selection.Copy
'当前工作簿新建一个表命名为“复制表存放”并粘贴
ThisWorkbook.Sheets.Add after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
Sheets(Sheets.Count).Name = "复制表存放"
ThisWorkbook.Sheets("复制表存放").Range("A1").Select
ActiveSheet.Paste
wdap.Documents.Close False '不保存关闭word表
wdap.Quit '退出Word应用,未保存且退出,相当于未留下Word文档的痕迹
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox "完成"
End Sub
借助VBA与Microsoft Office的协同功能,用户可构建高效且强大的跨应用程序自动化解决方案。该方案不仅支持与Word和PowerPoint之间的无缝协作,同时涵盖Office套件中的Outlook及Access等组件与Excel的协同作业。对于存在频繁跨应用操作需求的用户,建议系统研读相关技术文档,将有效解决实际工作中遇到的各类技术问题。
结束语
至此,基础的课程知识已全部分享完毕。衷心希望以上基于本人学习经历的总结,能够为你的VBA学习提供帮助,愿你的职业生涯中取得更大成就。
请开发者喝杯咖啡!