Excel开发:简化工作表中选定区域的操作。

By admin in 计算机教程 on 2019年11月16日

这是我第一次发Office开发相关的帖子。说到Office开发,我只能算新手。这次是碰巧开发了一个Excel智能文档项目,其中用到了这个小小的技巧,就发出来让大家看看。
在Excel开发中,工作表上最基本也是最常用的元素就是Range,Range可以表达一个获任意多个单元格或者矩形区域的组合,其复杂程度相当高。如果我们的智能文档程序要与用户打交道的话,势必要编程控制文档中的单元格,或与用户选择的单元格交互。而Range的对象模型并不符合.NET开发人员的习惯,要想获取用户选中区域的形状或者操作特定形状的区域都十分繁琐。而MSDN和VSTO的推销人员们只关心诸如怎么把单元格和数据源或者XML绑定之类,这种“小事”只能靠我们自己动手了。我的任务就是编写一个Range的封装类,将Range中所有单元格和矩形区域转化为易于访问的对象模型。首先我们看看Range的组成,一个普通的Range可以是一个或多个矩形区域的集合,每个矩形区域都由一组连续的列和连续的行组成。其中行使用阿拉伯数字索引,而列采用字母索引。如图所示:
图片 1
注意,多个矩形区域可以不连续,还可以交叠。每个Range都有一个描述其位置的字符串,称为Range的地址字符串。地址字符串不但包含所有位置信息,还可以被Excel用来直接快速定位,所以我们就以地址字符串为桥梁,编写我们的包装类。
ColumnWrapper类:主要用于吧表示列的字符串“A”,“B”,“AA”等转化为1开始的整数序列,或者相反。我这里用到的算法可以支持无限大的整数与列名字符串互转,但其实Excel只支持到256列。

 

图片 2图片 3ColumnWrapper
图片 4图片 5Public Class ColumnWrapperClass ColumnWrapper
图片 6
图片 7    Private Const c_NameChar = ” ABCDEFGHIJKLMNOPQRSTUVWXYZ”
图片 8    Private indexValue As Integer
图片 9图片 10    Public ReadOnly Property Index()Property Index() As Integer
图片 11        Get
图片 12            Return indexValue
图片 13        End Get
图片 14
图片 15    End Property
图片 16
图片 17    Private nameValue As String
图片 18图片 19    Public ReadOnly Property Name()Property Name() As String
图片 20        Get
图片 21            Return nameValue
图片 22        End Get
图片 23
图片 24    End Property
图片 25
图片 26图片 27    Public Sub New()Sub New(ByVal name As String)
图片 28
图片 29        nameValue = name
图片 30        indexValue = GetIndex(name)
图片 31    End Sub
图片 32
图片 33图片 34    Private Function GetName()Function GetName(ByVal i As Integer) As String
图片 35        Debug.Assert(i > 0)
图片 36
图片 37        Dim left As Integer = (i – 1) \ 26
图片 38        Dim right As Integer = i Mod 26
图片 39
图片 40        Dim nameLeft As String
图片 41        Dim nameRight As String
图片 42        If left > 0 Then
图片 43            nameLeft = GetName(left)
图片 44        Else
图片 45            nameLeft = String.Empty
图片 46        End If
图片 47
图片 48        If right = 0 Then right = 26
图片 49        nameRight = c_NameChar(right)
图片 50
图片 51        Return nameLeft & nameRight
图片 52    End Function
图片 53
图片 54图片 55    Private Function GetIndex()Function GetIndex(ByVal name As String) As Integer
图片 56        Debug.Assert(Not String.IsNullOrEmpty(name))
图片 57        Dim len As Integer = name.Length
图片 58        Dim bit As Integer = 1
图片 59
图片 60        For i As Integer = len – 1 To 0 Step -1
图片 61            Dim c As Char = name(i)
图片 62            Dim val = c_NameChar.IndexOf(Char.ToUpper(c))
图片 63
图片 64            GetIndex += bit * val
图片 65            bit *= 26
图片 66        Next
图片 67    End Function
图片 68
图片 69图片 70    Public Sub New()Sub New(ByVal index As Integer)
图片 71        nameValue = GetName(index)
图片 72        indexValue = index
图片 73    End Sub
图片 74
图片 75图片 76    Public Overrides Function GetHashCode()Function GetHashCode() As Integer
图片 77        Return indexValue
图片 78    End Function
图片 79
图片 80图片 81    Public Overrides Function Equals()Function Equals(ByVal obj As Object) As Boolean
图片 82        If obj Is Nothing Then Return False
图片 83
图片 84        Return TryCast(obj, ColumnWrapper).indexValue = indexValue
图片 85    End Function
图片 86
图片 87End Class

 

有了ColumnWrapper,下面就是CellWrapper,表示单个单元格。

上网最麻烦的事莫过于在地址栏中输入网址了。虽然有收藏夹帮忙,喜爱的网站多了它也日渐臃肿,占用资源不算,用起来也不是很方便。用VB做个“网络助手”吧!

图片 88图片 89CellWrapper
图片 90图片 91Public Class CellWrapperClass CellWrapper
图片 92
图片 93    Private addr As String
图片 94
图片 95图片 96    Public ReadOnly Property Address()Property Address() As String
图片 97        Get
图片 98            Return addr
图片 99        End Get
图片 100    End Property
图片 101
图片 102    Private rowValue As Integer
图片 103图片 104    Public ReadOnly Property RowIndex()Property RowIndex() As Integer
图片 105        Get
图片 106            Return rowValue
图片 107        End Get
图片 108    End Property
图片 109
图片 110    Private columnValue As ColumnWrapper
图片 111图片 112    Public ReadOnly Property Column()Property Column() As ColumnWrapper
图片 113        Get
图片 114            Return columnValue
图片 115        End Get
图片 116
图片 117    End Property
图片 118
图片 119    Private cellValue As String
图片 120图片 121    Public ReadOnly Property Value()Property Value() As String
图片 122        Get
图片 123            Return cellValue
图片 124        End Get
图片 125
图片 126    End Property
图片 127
图片 128图片 129    Public Sub New()Sub New(ByVal row As Integer, ByVal column As ColumnWrapper)
图片 130        rowValue = row
图片 131        columnValue = column
图片 132
图片 133        Me.addr = “$” & column.Name & “$” & row
图片 134    End Sub
图片 135
图片 136    Private Shared rx As New Regex(“\$([A-Z]+)\$([1-9][0-9]*)”, RegexOptions.Compiled)
图片 137
图片 138图片 139    Public Sub New()Sub New(ByVal address As String)
图片 140        Me.addr = address
图片 141
图片 142        Dim matches As MatchCollection = rx.Matches(address)
图片 143        If matches.Count = 0 Then Throw New ArgumentException(“address”)
图片 144
图片 145        Dim firstMatch As Match = matches(0)
图片 146
图片 147        Me.columnValue = New ColumnWrapper(firstMatch.Groups(1).Value)
图片 148        Me.rowValue = Integer.Parse(firstMatch.Groups(2).Value)
图片 149
图片 150    End Sub
图片 151
图片 152End Class

这个网络助手至少要实现这样的功能:双击用户界面的网站名称,就能调出浏览器并进入该网站。(当然,如果你愿意,还可以添加其它功能,如删除、修改、添加网址,自动拨号,计时等)构想是这样:用文本文档记录网站名称,程序运行时读取文本文档并在用户界面显示网站名,当用户双击网站名称时调出网址、链接。

接下来我们要表示矩形区域RectRangeWrapper。我们用来表示矩形区域。当我们需要计算矩形区域的大小时,只要使用着两个角单元格的位置信息即可算出。

为此,着手编程之前我们必须做两项准备工作:

图片 153图片 154RectRangeWrapper
图片 155图片 156Public Class RectRangeWrapperClass RectRangeWrapper
图片 157    Private addr As String
图片 158
图片 159图片 160    Public ReadOnly Property Address()Property Address() As String
图片 161        Get
图片 162            Return addr
图片 163        End Get
图片 164    End Property
图片 165
图片 166    Private isWide As Boolean = False
图片 167图片 168    Public ReadOnly Property IsWideRange()Property IsWideRange() As Boolean
图片 169        Get
图片 170            Return isWide
图片 171        End Get
图片 172    End Property
图片 173
图片 174
图片 175    Private leftTop As CellWrapper
图片 176图片 177    Public ReadOnly Property LeftTopCell()Property LeftTopCell() As CellWrapper
图片 178        Get
图片 179            Return leftTop
图片 180        End Get
图片 181    End Property
图片 182
图片 183
图片 184    Private rightBottom As CellWrapper
图片 185图片 186    Public ReadOnly Property RightBottomCell()Property RightBottomCell() As CellWrapper
图片 187        Get
图片 188            Return rightBottom
图片 189        End Get
图片 190
图片 191    End Property
图片 192
图片 193图片 194    Public ReadOnly Property Width()Property Width() As Integer
图片 195        Get
图片 196            Return Me.rightBottom.Column.Index – Me.leftTop.Column.Index + 1
图片 197        End Get
图片 198    End Property
图片 199
图片 200图片 201    Public ReadOnly Property Height()Property Height() As Integer
图片 202        Get
图片 203            Return Me.rightBottom.RowIndex – Me.leftTop.RowIndex + 1
图片 204        End Get
图片 205    End Property
图片 206
图片 207图片 208    Public Sub New()Sub New(ByVal address As String)
图片 209
图片 210        If address.IndexOf(“:”c) > 0 Then
图片 211            Dim cells() As String = address.Split(“:”c)
图片 212
图片 213            Try
图片 214                leftTop = New CellWrapper(cells(0))
图片 215                rightBottom = New CellWrapper(cells(1))
图片 216            Catch ex As ArgumentException
图片 217                ‘wide range selected:
图片 218
图片 219                leftTop = Nothing
图片 220                rightBottom = Nothing
图片 221                Me.isWide = True
图片 222            End Try
图片 223        Else
图片 224            If address Like “$[A-Z,a-z]*$[1-9]*” Then
图片 225                leftTop = New CellWrapper(address)
图片 226                rightBottom = leftTop
图片 227            End If
图片 228        End If
图片 229
图片 230        Me.addr = address
图片 231    End Sub
图片 232End Class

一.用记事本编写一个名为 homepage 的 TXT
文档。每行写一个网站名称,不要有空行。

最后是多个矩形区域组成的完整Range,我们用MultiRectRangeWrapper类来描述。它其实就是一个矩形区域的集合。

二.用数据库程序 Access (Office组件之一) 建立一个名为
address 的数据库,表名为 net,主字段名为
netaddress。给数据库输入记录:按照 homepage.txt
文档中的网站顺序写好各网站主页的详细网址,结束后存盘退出。

图片 233图片 234MultiRectRangeWrapper
图片 235图片 236Public Class MultiRectRangeWrapperClass MultiRectRangeWrapper
图片 237
图片 238    Private rectRanges As List(Of RectRangeWrapper)
图片 239图片 240    Public ReadOnly Property Areas()Property Areas() As List(Of RectRangeWrapper)
图片 241        Get
图片 242            Return rectRanges
图片 243        End Get
图片 244    End Property
图片 245
图片 246
图片 247    Private ReadOnly addr As String
图片 248图片 249    Public ReadOnly Property Address()Property Address() As String
图片 250        Get
图片 251            Return addr
图片 252        End Get
图片 253    End Property
图片 254
图片 255
图片 256图片 257    Public Sub New()Sub New(ByVal address As String)
图片 258        addr = address
图片 259        rectRanges = New List(Of RectRangeWrapper)
图片 260
图片 261        ‘parse the ranges
图片 262        Dim numberOfRanges As Integer
图片 263        Dim rectRangeAddr As String()
图片 264
图片 265        If addr.IndexOf(“,”) < 0 Then
图片 266            rectRangeAddr = New String(0) {addr}
图片 267            numberOfRanges = 1
图片 268        Else
图片 269            rectRangeAddr = addr.Split(“,”)
图片 270            numberOfRanges = rectRangeAddr.Length
图片 271        End If
图片 272
图片 273        For Each r As String In rectRangeAddr
图片 274            rectRanges.Add(New RectRangeWrapper(r))
图片 275        Next
图片 276    End Sub
图片 277End Class

 

好了,现在四个包装类已经全部写完了。用法就简单多了。用Range.Address属性初始化MultiRectRangeWrapper类,就可以得到一个该Range中所有矩形区域的集合,进而轻松访问该举行区域中每个单元格。比如下面这个例子,展示了一些简单的区域操作:

现在可以进入具体编程了。

‘取得用户选定的区域
Dim myrange As New MultiRectRangeWrapper(Application.Selection.Address)
For Each rectRange As RectRangeWrapper In myrange.Areas
    If Not rectRange.IsWideRange Then
        ‘非扩展选择的区域,比如整行或整列
        ‘修改左上角的数字格式
        Me.Range(rectRange.LeftTopCell.Address).NumberFormat = “0.00”

这个程序所需控件不多:一个 data 控件,一个 ListBox
控件和一个 Label 控件即可。在属性窗口将 data
控件与库文件及其表链接好,并将 Label 控件与 Data
控件绑定。接着调整一下各控件的位置和大小。

        ‘将最后一个选中区域复制到剪贴版
        Me.Range(rectRange.Address).Cut()
    End If
Next

 

我写这个只是为了我自己写程序方便,并没有考虑太多因素,所以可能写得比较粗糙,功能有限。有兴趣的可以在我这程序的基础上继续改进。

下面是具体的代码,我将在代码中穿插作些必要的解释:

 

Option Explicit

调用浏览器的API

Private Declare Function ShellExecute Lib
“shell32.dll” Alias “ShellExecuteA” (ByVal hwnd As Long, ByVal
lpOperation As String, ByVal lpFile As String, ByVal lpParameters As
String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As
Long

Dim Sort As String 申明选择类别

Dim address As String 申明网址

Dim addresslink 申明网址链接

Dim AllLines As New Collection
内存中的行数据库(你可以看得出来,模仿了”日积月累”的代码来实现对文档文档的读取和显示)

Dim CurrentLine As Long 当前行集合索引

 

链接网址声明

Private Sub Link()

address = ShellExecute(0&, vbNullString, address,
vbNullString, vbNullString, vbNormalFocus)

End Sub

 

Form_Load 事件

Private Sub Form_Load()

Data1.DatabaseName = App.Path + “address.mdb”

定位库文件(虽然在属性中已经绑定了数据库,为使程序能在别的机器上正常运行,这行是有必要的)

Data1.RecordSource = “net” 字段

Data1.Visible = False data控件不可见

Dim nextLine As String 从文件中读出的每一行

Dim InFile As Integer 文件的描述符

InFile = FreeFile

Open App.Path + “homepage.txt” For Input As InFile
打开文件

While Not EOF(InFile)

Line Input #InFile, nextLine

AllLines.Add nextLine

Wend

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 澳门金沙30064在线网站 版权所有