Excel开发学习笔记:界面交互与控件的布局
遇到一个数据处理自动化的问题,于是打算开发一个基于excel的小工具。在业余时间一边自学一边实践,抽空把一些知识写下来以备今后参考,因为走的是盲人摸象的野路子,幼稚与错误请多包涵。
开发环境基于VSTO,具体配置:visual studio 2010,VB .Net,excel 2007,文档级别的定制程序。
除了业务逻辑之外,比较耗时耗力的就是人机交互了。在编写excel定制程序的过程中,这次用到了以下几种交互方式:
- 通过excel工作表(worksheet)获取用户输入
- 通过按钮控件触发功能代码执行
- 通过TreeView控件显示内容摘要及导航
- 通过自定义的windows窗体提供交互
工作表的操作放到后面再说,讲一下按钮控件button和Treeview控件的布局。
放置控件的方法:
无非可视化放置和运行时代码加载两种。可视化放置比较直观,通过visual studio的toolbox工具栏拖动需要的控件到工作表上或者自定义的windows窗体上,然后可以在设计界面设置控件的各种参数,在程序启动时自 动加载。代码加载则需要去msdn上查找各种控件开放的API接口,然后写代码调用API,在程序运行时由这些代码完成加载和参数设置。
控件的布局:
常见的有以下几种位置可供选择,适用于不同情况。
- excel worksheet上
- 操作窗格action pane(文档级自定义程序专用)
- Ribbon UI功能区(也就是excel2007上方的"开始"、"插入"菜单的位置)
另外,我们还可以在弹出的自定义windows窗体在上布局各种控件。Office UI自定义的官方介绍点击打开链接:http://msdn.microsoft.com/zh-cn/library/bf08984t.aspx
个人觉得操作窗格是很好用的一个布局区域,它通常位于office界面的侧面,类似windows文件夹界面左侧的导航栏,优势是区域够大且不影响 excel工作表界面。很多文章提到自定义任务窗格custom task pane的用法,我迷糊了好久才明白这东西是给外接程序add-ins用的,对应文档级程序中的操作窗格action pane,显示效果差不多。
1、工作表worksheet
最简单粗暴而有效的布局方式,将工作表作为放置控件的容器,直接从控件工具栏中拖动想要的控件到指定的位置,然后双击控件就可切换到代码视图,在想要的事件中添加功能代码即可。而且因为控件的事件代码是和工作表代码一起的,访问工作表的内容特别方便,很贴心。
比如下面的代码响应某个按钮的点击,直接给按钮所在工作表的第二行第一列的单元格赋值,内容是当前工作表的名称。
Cells(2, 1).value = Name
MsgBox(Name)
End Sub
这种控件布局方式的缺点主要是干扰了工作表界面的正常操作,与工作表随心所欲的多变特性不般配,而且显示效果比较丑陋。适合快速开发的小工具,简单有效地解决业务问题即可。我最早也是将按控件放置在工作表上,后来都移走了。
2、操作窗格action pane操作窗格没法在设计界面编辑,无法向其中拖动控件,只能写代码来完成控件的加载和设置。但我们可以在工程中添加一个用户控件(user control)或者操作窗格控件(action pane control),这2个control控件的作用就是作为其他控件的容器,分别位于工程右键菜单的add-->new item-->windows forms和new item-->office下面。可以向他们上面拖动你想要的其他控件,根据喜好设置好界面后,只需要用代码把control控件加载到操作窗格即 可,所有的内容都会自动显示出来。
加载控件到操作窗格的代码如下(以工作薄workbook中加载为例):
'在过程外部声明添加到工程的control控件,作为类变量
Public actionControl As New ActionsPaneControl1
Private Sub ThisWorkbook_Startup() Handles Me.Startup
'加载control控件到操作窗格,ActionsPane要通过工作簿workbook来引用
ActionsPane.Controls.Add(actionControl)
'设置操作窗格的显示位置为左侧,注意获取操作窗格与添加control控件的差别
Application.CommandBars("Task Pane").Position = Microsoft.Office.Core.MsoBarPosition.msoBarLeft
End Sub
End Class
如果在worksheet代码中添加控件到操作窗格,需要通过Globals.ThisWorkbook.ActionsPane来引用。
效果图:
3、Ribbon UI功能区
office2007 自带的功能入口所在的区域,几经调整我最终把功能按钮放在了ribbon功能区。可以通过工程右键菜单-->add-->new item-->office-->ribbon新增自定义Ribbon功能区(我选择了visual designer模版)。然后就可以在设计器界面往新增的自定义功能区上添加控件了。
修改控件的大小:
属性窗口-->controlSize,默认小号
修改控件的图标:
属性窗口-->OfficeImageId,可以通过名称关联Office自带的图标,省去了自己找图标的麻烦。比如输入FileOpen就会在运行时显示为平时常见的打开文件的图标。可以在微软官网http://www.microsoft.com/en-us/download/details.aspx?id=11675下载到所有office图标所对应的OfficeImageId,也就是2007 Office System Add-In: Icons Gallery。
修改功能区的位置:
属性窗口-->position-->PositionType,默认是放在最后面,在我机器上也就是在"开发工具"功能区的后面。还可以选择 “beforeOfficeId”和“afterOfficeId”,分别表示我们的功能区域希望放在哪个功能区的前面或后面,这时还需要输入这个功能区 的officeId或名称。可以在微软官网http://www.microsoft.com/en-us/download/details.aspx?id=6627下载到所有office自带控件的详细情况,也就是Office 2010 Help Files: Office Fluent User Interface Control Identifiers。
这个帮助文档的内容实在太多,可以通过control Type列筛选“tab”关键字,可以发现这些ribbon功能区的命名规则很简单,Tab+功能区英文名,明白这个规则后可以省去查找的工作,比如“开 始”和“插入”功能区在excel英文版中显示为“Home”和“Insert”,那么我们只需要在OfficeId栏中输入TabHome和 TabInsert即可。如果想把自定义功能区显示在最前面,只需要选择beforeOfficeId,然后输入TabHome即可。
另外,VS2010的Ribbon设计器有bug,照上面的方法在界面设置beforeOfficeId和TabHome属性之后,运行会报错“The type 'Microsoft.VisualStudio.Tools.Office.Ribbon.View.GenericRibbonView' has no property named 'Factory'.”。还好Msdn社区有帖子说这个事,链接在此点击打开链接。好像是说由于vs2010在后台自动生成的ribbon界面代码不正确,报错位于ribbon1.designer.vb中的InitializeComponent函数内(ribbon1为添加ribbon功能区时的代码名称),将错误的语句剪切到InitializeComponent 函数的调用之后即可,比如把“Me.Tab1.Position = Me.Factory.RibbonPosition.BeforeOfficeId("TabHome")”剪切到下图位置。在我的工程中,这个位置就在错误语句的上方不远。
Public Sub New()
MyBase.New(Globals.Factory.GetRibbonFactory())
'This call is required by the Component Designer.
InitializeComponent()
'移到这里
Me.Tab1.Position = Me.Factory.RibbonPosition.BeforeOfficeId("TabHome")
End Sub
效果图: