Range.Areas に多数のセル領域を(統合されることなく)セットするためのクラスモジュール例。
※ Union だと統合されてしまう/Range.Address には一定長以上の文字列はセットできない、という制限を、名前(Workbook.Names)経由で設定することで回避する試み。
Last active
April 21, 2020 23:16
-
-
Save furyutei/495fa487d57027d0db29eb7d7ae59bb2 to your computer and use it in GitHub Desktop.
ExcelのRange.Areasに多数のセル領域をセット
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
Private Const NameLengthLimit = 260 ' Name.RefersTo に設定可能な長さ | |
Private NamePrefix As String | |
Private NameNumber As Long | |
Private TargetWorkbook As Workbook | |
Private ClearNamesAuto As Boolean | |
Private AddSheetPrefix As Boolean | |
Private IsRowAbsolute As Boolean | |
Private IsColumnAbsolute As Boolean | |
Private FirstRange As Range | |
Private AreaAddresses() As String | |
Private LastCollectedAddressIndex As Long | |
Public Sub Init( _ | |
Optional targetBook As Workbook, _ | |
Optional clearStoredNames As Boolean = False, _ | |
Optional clearAuto As Boolean = True, _ | |
Optional storedNamePrefix As String = "_", _ | |
Optional sheetPrefix As Boolean = False, _ | |
Optional absoluteRow As Boolean = True, _ | |
Optional absoluteColumn As Boolean = True _ | |
) | |
NamePrefix = storedNamePrefix | |
NameNumber = 0 | |
If targetBook Is Nothing Then Set targetBook = ActiveWorkbook | |
Set TargetWorkbook = targetBook | |
ClearNamesAuto = clearAuto | |
AddSheetPrefix = sheetPrefix | |
IsRowAbsolute = absoluteRow | |
IsColumnAbsolute = absoluteColumn | |
Set FirstRange = Nothing | |
ReDim AreaAddresses(0) | |
LastCollectedAddressIndex = 0 | |
If clearStoredNames Then Clear ' TODO: 既存の名前削除には時間がかかる | |
End Sub | |
Public Sub Clear() | |
Dim savedCalculation As Long: savedCalculation = Application.Calculation | |
Application.Calculation = xlCalculationManual | |
Dim currentName As Name | |
Dim reg As Object: Set reg = CreateObject("VBScript.RegExp") | |
With reg | |
.Global = True | |
.Pattern = "(?:^|!)" & NamePrefix & "\d+$" | |
End With | |
On Error Resume Next | |
For Each currentName In TargetWorkbook.Names | |
If 0 < reg.Execute(currentName.Name).Count Then currentName.Delete | |
Next | |
On Error GoTo 0 | |
NameNumber = 0 | |
LastCollectedAddressIndex = 0 | |
Application.Calculation = savedCalculation | |
End Sub | |
Public Sub AddArea(addCells As Range) | |
If Not addCells.Worksheet.Parent Is TargetWorkbook Then | |
Err.Raise 8888, addCells, "Specified areas do not belong to target workbook" | |
Exit Sub | |
End If | |
Dim sheetPrefix As String | |
Dim cellRange As Range | |
Dim addressIndex As Long: addressIndex = UBound(AreaAddresses) | |
ReDim Preserve AreaAddresses(UBound(AreaAddresses) + addCells.Areas.Count) | |
If FirstRange Is Nothing Then Set FirstRange = addCells | |
For Each cellRange In addCells.Areas | |
addressIndex = addressIndex + 1 | |
sheetPrefix = IIf(AddSheetPrefix, "'" & cellRange.Worksheet.Name & "'!", "") | |
AreaAddresses(addressIndex) = sheetPrefix & cellRange.Address(IsRowAbsolute, IsColumnAbsolute) | |
Next | |
End Sub | |
Public Property Get CollectedRange() As Range | |
Dim savedCalculation As Long: savedCalculation = Application.Calculation | |
Application.Calculation = xlCalculationManual | |
If UBound(AreaAddresses) = LastCollectedAddressIndex Then GoTo SET_RESULT | |
Dim addressIndex As Long | |
Dim nameLength As Long | |
Dim currentAddress As String | |
Dim nameParts() As String | |
Dim setIndex As Long | |
nameLength = 0 | |
ReDim nameParts(1 To UBound(AreaAddresses)) | |
setIndex = 0 | |
If LastCollectedAddressIndex = 0 Then | |
NameNumber = 0 | |
Else | |
nameParts(1) = NamePrefix & NameNumber | |
nameLength = Len(nameParts(1)) + 1 | |
setIndex = 1 | |
End If | |
For addressIndex = LastCollectedAddressIndex + 1 To UBound(AreaAddresses) | |
currentAddress = AreaAddresses(addressIndex) | |
nameLength = nameLength + Len(currentAddress) + 1 ' +1 は "=" もしくは "," の分 | |
If nameLength <= NameLengthLimit Then | |
setIndex = setIndex + 1 | |
nameParts(setIndex) = currentAddress | |
Else | |
ReDim Preserve nameParts(1 To setIndex) | |
NameNumber = NameNumber + 1 | |
setAreaName NamePrefix & NameNumber, "=" & Join(nameParts, ",") | |
ReDim nameParts(1 To UBound(AreaAddresses)) | |
nameParts(1) = NamePrefix & NameNumber | |
nameParts(2) = currentAddress | |
nameLength = Len(nameParts(1)) + Len(currentAddress) + 2 | |
setIndex = 2 | |
End If | |
Next | |
If 0 < setIndex Then | |
ReDim Preserve nameParts(1 To setIndex) | |
NameNumber = NameNumber + 1 | |
setAreaName NamePrefix & NameNumber, "=" & Join(nameParts, ",") | |
End If | |
LastCollectedAddressIndex = UBound(AreaAddresses) | |
SET_RESULT: | |
If 0 < NameNumber And FirstRange.Worksheet.Parent Is TargetWorkbook Then | |
Set CollectedRange = FirstRange.Worksheet.Range(NamePrefix & NameNumber) | |
Else | |
Set CollectedRange = Nothing | |
End If | |
If ClearNamesAuto Then Clear | |
Application.Calculation = savedCalculation | |
End Property | |
'Private Sub setAreaName(key As String, value As String) | |
' On Error Resume Next | |
' TargetWorkbook.Names(key).Delete | |
' TargetWorkbook.Names.Add Name:=key, RefersTo:=value | |
' On Error GoTo 0 | |
'End Sub | |
Private Sub setAreaName(key As String, value As String) | |
On Error GoTo ADD_NAME | |
TargetWorkbook.Names(key).RefersTo = value | |
Exit Sub | |
ADD_NAME: | |
TargetWorkbook.Names.Add Name:=key, RefersTo:=value | |
End Sub | |
Private Sub Class_Initialize() | |
Init | |
End Sub | |
Private Sub Class_Terminate() | |
Clear | |
End Sub |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
Sub Test_AreaCollection() | |
ThisWorkbook.Activate | |
Dim ac As New AreaCollection | |
Dim counter As Long | |
Dim maxNumber As Long: maxNumber = 10000 | |
Dim resultRange As Range | |
Dim startTime As Double | |
Dim markTime As Double | |
Dim stopTime As Double | |
Application.ScreenUpdating = False | |
Application.EnableEvents = False | |
'Application.Calculation = xlCalculationManual | |
With ac | |
.Init clearAuto:=False | |
' clearAuto: .CollectedRange 取得時 Names を自動クリア(True:する/False:しない) | |
ActiveSheet.Cells.Interior.ColorIndex = 0 | |
startTime = Timer | |
markTime = startTime | |
.AddArea Range("A1") | |
.AddArea Range("A2") | |
.AddArea Range("A3") | |
.AddArea Range("B3") | |
.AddArea Range("A4") | |
.AddArea Range("C1") | |
.AddArea Range("C4") | |
.AddArea Range("A5:C5") | |
.AddArea Range("A6:C6") | |
Set resultRange = .CollectedRange | |
resultRange.Select | |
Debug.Print "(*) Areas: " & resultRange.Areas.Count & " Names: " & ActiveWorkbook.Names.Count | |
For counter = 10 To maxNumber | |
.AddArea Cells(counter, 1 + counter Mod 10) | |
Next | |
Debug.Print "AddArea: " & Format$(Timer - markTime, "0.00") & " sec." | |
markTime = Timer | |
Set resultRange = .CollectedRange | |
resultRange.Interior.Color = RGB(255, 255, 0) | |
resultRange.Select | |
stopTime = Timer | |
Debug.Print "Collect: " & Format$(stopTime - markTime, "0.00") & " sec." | |
Debug.Print "Total : " & Format$(stopTime - startTime, "0.00") & " sec." | |
Debug.Print "(*) Areas: " & resultRange.Areas.Count & " Names: " & ActiveWorkbook.Names.Count | |
'Debug.Print " Address: " & resultRange.Address | |
Debug.Print String(80, "-") | |
End With | |
'Application.Calculation = xlCalculationAutomatic | |
Application.EnableEvents = True | |
Application.ScreenUpdating = True | |
End Sub |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
■ 元ネタ
■ 関連