利用vba实现将excel表格左转90度复制到指定位置
在编辑较长word文档的时候,如果文件中有很宽的表格,这时一般需要在文档中插入分节符,将插入表格的那一节设置为横向页面。如果能够将横向表格直接转换成纵向表格,那么就不需改变页面方向,插入页码页眉页脚什么的都比较方便。在word中手工将一个横向表格改成纵向,不但输入工作量大,也容易出错。比较方便的方法是将表格拷贝到excel中,用VBA宏把表格左旋90度,修改单元格文字方向后再拷回word。以下宏代码可将excel表格中某个区域的内容左旋90度复制到指定的其他区域,可供参考:
Sub test()
Dim srcTotalColumns As Integer '源区域总列数,多列合并为1列的,仍然算多列
Dim srcTotalRows As Integer '源区域总行数,多行合并为1行的,仍然算多行
Dim srcStartRow As Integer '源区域左上单元格行号
Dim srcStartColumn As Integer '源区域左上单元格列号
Dim destStartColumn As Integer '目标区域左上单元格列号
Dim destStartRow As Integer '目标区域左上单元格行号
'临时变量,i、j分别是目标区域行列指针,m、n分别是源区域行列指针
Dim i As Integer, j As Integer, m As Integer, n As Integer
'根据实际情况给各变量赋值。这个宏移植到其他地方使用时只需修改以下六个变量的赋值
srcTotalColumns = 6: srcTotalRows = 4: srcStartColumn = 3: srcStartRow = 5
destStartColumn = 12: destStartRow = 7
'源区域由右上角开始遍历
m = srcStartRow
n = srcStartColumn + srcTotalColumns -1
'目标区域由左上角开始遍历。向左旋转90度后源区域右上角单元格刚好移动到目标区域左上角单元格
For j = destStartColumn To (destStartColumn + srcTotalRows - 1)
For i = destStartRow To (destStartRow + srcTotalColumns - 1)
ActiveSheet.Cells(i, j) = "=" & ColNum2Letter (n) & m
n = n - 1
Next i
m = m + 1
n = srcStartColumn + srcTotalColumns -1
Next j
'目标区域由左上角开始遍历。 采取while循环,与上面的for循环等价,注释掉
'j = destStartColumn
' While (j < (destStartColumn + srcTotalRows))
' i = destStartRow
' While (i < (destStartRow + srcTotalColumns))
' ActiveSheet.Cells(i, j) = "=" & ColNum2Letter (n) & m
' n = n - 1: i = i + 1
' Wend
' m = m + 1
' n = srcStartColumn + srcTotalColumns - 1
' j = j + 1
' Wend
End Sub
'用辗转相除法将数字列号转换为字母列号的函数,例如列“100”转换为"CV",ByVal重要,防止参数值被修改
Function ColNum2Letter(ByVal iCol As Integer) As String
Dim i As Integer, j As Integer
Dim res(4) As String 'excel当前发行版本中最大列号只有三个字母,4个元素的数组足够了
i = 0
While (iCol > 0)
i = i + 1
If (iCol Mod 26) = 0 Then
res(i) = "Z"
Else
res(i) = Chr((iCol Mod 26) + 64)
End If
If (iCol Mod 26) = 0 Then
iCol = Int(iCol / 26) - 1
Else
iCol = Int(iCol / 26)
End If
Wend
For j = i To 1 Step -1
ColNum2Letter = ColNum2Letter & res(j)
Next j
End Function
这个宏的好处是以后修改表格中横向区域的内容时纵向区域的内容会自动跟着更新,只要区域大小和结构不发生变化,不需再次运行宏,就可以直接拷贝纵向区域的内容。但是,源区域中的空白单元格复制到目标区域后会显示0,源区域中的合并单元格拷贝到目标区域后还需要手工合并,这些都需要进一步完善。
上述宏运行示例效果图(目标区域中的那些0以及合并单元格的情况尚需手工处理):
如果源区域行列数难数,则只需选择源区域,运行下面的代码记下行列数即可:
MsgBox "列数=" & Selection.Columns.Count & vbCrLf & "行数=" & Selection.Rows.Count
继续改进,这次的代码只需选中源区域后运行即可,移植时不需再修改代码。提醒用户输入指定目标区域左上单元格时,给出的参考单元格是源区域右下单元格斜右下方空出一行一列的位置,主要是为了设置目标区域文字方向后调整单元格高度与宽度时不影响源区域的单元格高度与宽度。用户需注意目标区域不要有数据,否则会被破坏。
Sub excel左旋90度拷贝()
Dim srcTotalColumns As Integer '源区域总列数,多列合并为1列的,仍然算多列
Dim srcTotalRows As Integer '源区域总行数,多行合并为1行的,仍然算多行
Dim srcStartRow As Integer '源区域左上单元格行号
Dim srcStartColumn As Integer '源区域左上单元格列号
Dim destStartColumn As Integer '目标区域左上单元格列号
Dim destStartRow As Integer '目标区域左上单元格行号
'临时变量,i、j分别是目标区域行列指针,m、n分别是源区域行列指针
Dim i As Integer, j As Integer, m As Integer, n As Integer
'记录合并单元格所占的行数和列数的变量,未合并的单元格视为占1行1列
Dim mergeRows As Integer, mergeColumns As Integer
mergeRows = 1: mergeColumns = 1
'必须将源区域全部选中后再运行这个宏,根据选择区域自动初始化源区域有关参数
srcTotalColumns = Selection.Columns.Count
srcTotalRows = Selection.Rows.Count
srcStartColumn = Selection.Column
srcStartRow = Selection.Row
destStartColumn = Application.InputBox(Prompt:="请输入目标区域左上单元格列号,参考列号为" & _
srcStartColumn + srcTotalColumns + 1, Title:="输入列号", Type:=1)
destStartRow = Application.InputBox(Prompt:="请输入目标区域左上单元格行号,参考行号为" & _
srcStartRow + srcTotalRows + 1, Title:="输入行号", Type:=1)
'源区域由右上角开始按行遍历,即自右向左,自上向下
m = srcStartRow
n = srcStartColumn + srcTotalColumns - 1
'目标区域由左上角开始按行遍历。向左旋转90度后源区域右上角单元格刚好移动到目标区域左上角单元格
j = destStartColumn
While (j < (destStartColumn + srcTotalRows))
i = destStartRow
While (i < (destStartRow + srcTotalColumns))
'源区域当前单元格为合并单元格且其上面的单元格地址与当前单元格的地址不同(即不在同一个合并区域中),则目标区域执行合并操作
If ActiveSheet.Cells(m, n).MergeCells Then
mergeRows = ActiveSheet.Cells(m, n).MergeArea.Rows.Count
mergeColumns = ActiveSheet.Cells(m, n).MergeArea.Columns.Count
If m = 1 Then
ActiveSheet.Range(ColNum2Letter(j) & i & ":" & ColNum2Letter(j + mergeRows - 1) & (i + mergeColumns - 1)).Merge
Else
If ActiveSheet.Range(ColNum2Letter(n) & m).MergeArea.Address <> _
ActiveSheet.Range(ColNum2Letter(n) & (m - 1)).MergeArea.Address Then
ActiveSheet.Range(ColNum2Letter(j) & i & ":" & ColNum2Letter(j + mergeRows - 1) & (i + mergeColumns - 1)).Merge
End If
End If
Else
mergeRows = 1: mergeColumns = 1
End If
If mergeColumns > 1 Then
n = n - mergeColumns + 1
End If
'下面这样可以保留引用关系,运行一次宏后,只要源区域表格结构不变,修改源区域后目标区域自动更新,不需再运行宏
ActiveSheet.Cells(i, j) = "=" & ColNum2Letter (n) & m
n = n - 1
i = i + mergeColumns
Wend
m = m + 1
n = srcStartColumn + srcTotalColumns - 1
j = j + 1
Wend
End Sub
顺便再记录一个函数,不过这个函数实用性不大,完全可以不用函数调用,而直接使用类似代码:
'将字母列号转为数字。用65536,是因为那个单元格不大可能被使用,不会由于单元格合并导致column属性值不对
Function ColLetter2Num(colLetter As String) As Integer
ColLetter2Num = Range(colLetter & 65536).Column
End Function
|