【发布时间】:2010-11-06 15:07:52
【问题描述】:
当我通过名称管理器创建命名范围时,我可以选择指定工作簿或[worksheet name] 范围。但是,如果随后想要更改范围,下拉菜单将显示为灰色。有没有办法在名称管理器或最好的 VBA 中更改现有命名范围的范围?
例如:
-
testName指的是'sheet1'!A1:B2范围工作簿。我将如何将其更改为 -
testName指的是'sheet1'!A1:B2范围为“sheet1”?
【问题讨论】:
当我通过名称管理器创建命名范围时,我可以选择指定工作簿或[worksheet name] 范围。但是,如果随后想要更改范围,下拉菜单将显示为灰色。有没有办法在名称管理器或最好的 VBA 中更改现有命名范围的范围?
例如:
testName 指的是 'sheet1'!A1:B2 范围工作簿。我将如何将其更改为testName 指的是 'sheet1'!A1:B2 范围为“sheet1”?【问题讨论】:
我找到了解决方案!只需使用您的命名变量复制工作表。然后删除原来的工作表。复制的工作表现在将具有相同的命名变量,但具有本地范围(范围=复制的工作表)。
但是,我不知道如何从局部变量更改为全局变量..
【讨论】:
您可以从以下网址下载我和 Jan Karel Pieterse 开发的免费名称管理器插件 http://www.decisionmodels.com/downloads.htm 这实现了 Excel 2007 名称管理器无法处理的许多名称操作,包括更改名称范围。
在 VBA 中:
Sub TestName()
Application.Calculation = xlManual
Names("TestName").Delete
Range("Sheet1!$A$1:$B$2").Name = "Sheet1!TestName"
Application.Calculation = xlAutomatic
End Sub
【讨论】:
"'Sheet1'!TestName" 我建议始终在代码中转义名称。如果不需要,也不会伤害任何东西。
查看这两个相互反转的子项,并翻转引用活动工作表上范围的所有命名范围的范围(工作表到工作簿或反转)。
Option Explicit
'---------------------------------------------------------------------------------------
' Procedure : RescopeNamedRangesToWorkbook
' Author : JS20'07'11
' Date : 11/18/2013
' Purpose : Rescopes the parent of worksheet scoped named ranges to the active workbook
' for each named range with a scope equal to the active sheet in the active workbook.
'---------------------------------------------------------------------------------------
Public Sub RescopeNamedRangesToWorkbook()
Dim wb As Workbook
Dim ws As Worksheet
Dim objName As Name
Dim sWsName As String
Dim sWbName As String
Dim sRefersTo As String
Dim sObjName As String
Set wb = ActiveWorkbook
Set ws = ActiveSheet
sWsName = ws.Name
sWbName = wb.Name
'Loop through names in worksheet.
For Each objName In ws.Names
'Check name is visble.
If objName.Visible = True Then
'Check name refers to a range on the active sheet.
If InStr(1, objName.RefersTo, sWsName, vbTextCompare) Then
sRefersTo = objName.RefersTo
sObjName = objName.Name
'Check name is scoped to the worksheet.
If objName.Parent.Name <> sWbName Then
'Delete the current name scoped to worksheet replacing with workbook scoped name.
sObjName = Mid(sObjName, InStr(1, sObjName, "!") + 1, Len(sObjName))
objName.Delete
wb.Names.Add Name:=sObjName, RefersTo:=sRefersTo
End If
End If
End If
Next objName
End Sub
'---------------------------------------------------------------------------------------
' Procedure : RescopeNamedRangesToWorksheet
' Author : JS20'07'11
' Date : 11/18/2013
' Purpose : Rescopes each workbook scoped named range to the specific worksheet to
' which the range refers for each named range that refers to the active worksheet.
'---------------------------------------------------------------------------------------
Public Sub RescopeNamedRangesToWorksheet()
Dim wb As Workbook
Dim ws As Worksheet
Dim objName As Name
Dim sWsName As String
Dim sWbName As String
Dim sRefersTo As String
Dim sObjName As String
Set wb = ActiveWorkbook
Set ws = ActiveSheet
sWsName = ws.Name
sWbName = wb.Name
'Loop through names in worksheet.
For Each objName In wb.Names
'Check name is visble.
If objName.Visible = True Then
'Check name refers to a range on the active sheet.
If InStr(1, objName.RefersTo, sWsName, vbTextCompare) Then
sRefersTo = objName.RefersTo
sObjName = objName.Name
'Check name is scoped to the workbook.
If objName.Parent.Name = sWbName Then
'Delete the current name scoped to workbook replacing with worksheet scoped name.
objName.Delete
ws.Names.Add Name:=sObjName, RefersTo:=sRefersTo
End If
End If
End If
Next objName
End Sub
【讨论】:
另一种方法是“破解”2007 或更高版本的 Excel 文件,但建议您在这样做时要小心,并保留原始文件的备份:
首先将 Excel 电子表格保存为 .xlsx 或 .xlsm 文件(不是二进制文件)。将文件重命名为 .zip,然后解压缩。转到 zip 结构中的 xl 文件夹,然后在写字板或类似的文本编辑器中打开 workbook.xml。命名范围可在 definedName 标记中找到。本地范围由 localSheetId="x" 定义(可以通过在 Excel 中按 Alt-F11 找到工作表 ID,打开电子表格,进入 VBA 窗口,然后查看“项目”窗格)。隐藏范围由 hidden="1" 定义,因此只需删除 hidden="1" 即可取消隐藏。
现在重新压缩文件夹结构,注意保持文件夹结构的完整性,然后重命名为 .xlsx 或 .xlsm。
如果您需要更改范围或隐藏/取消隐藏大量已定义范围,这可能不是最佳解决方案,尽管它可以很好地进行一两个小调整。
【讨论】:
从头开始创建新名称并删除旧名称。
【讨论】:
在尝试使用Workbook 范围定义命名范围时,这些答案有助于解决类似问题。 “啊哈!”对我来说是使用与整个工作簿相关的Names Collection!这可能对许多人来说是显而易见的,但在我的研究中并没有明确说明,所以我分享给其他有类似问题的人。
' Local / Worksheet only scope
Worksheets("Sheet2").Names.Add Name:="a_test_rng1", RefersTo:=Range("A1:A4")
' Global / Workbook scope
ThisWorkbook.Names.Add Name:="a_test_rng2", RefersTo:=Range("B1:b4")
如果您在 Sheet2 处于活动状态时查看名称列表,则两个范围都存在,但切换到任何其他工作表,"a_test_rng1" 不存在。
现在我可以很高兴地在我的代码中生成一个我认为合适的命名范围。无需乱用名称管理器或插件。
除此之外,Excel Mac 2011 中的名称管理器一团糟,但我确实发现虽然没有列标签可以告诉您在查看命名范围列表时正在查看的内容,但如果列出了工作表除了名称之外,该名称的范围是工作表/本地。请参阅随附的屏幕截图。
感谢this article 拼凑这些部分。
【讨论】:
以下是我将所有工作表名称提升为全局名称的方法。 YMMV
For Each wsh In ActiveWorkbook.Worksheets
For Each n In wsh.Names
' Get unqualified range name
Dim s As String
s = Split(n.Name, "!")(UBound(Split(n.Name, "!")))
' Add to "Workbook" scope
n.RefersToRange.Name = s
' Remove from "Worksheet" scope
Call n.Delete
Next n
Next wsh
【讨论】:
Dim wsh As Worksheet Dim N As Object
Name 引用相同的范围。
这仍然需要进一步完善,但适用于所有简单的引用,而不会杀死现有的本地名称。
Type GlobalNamesToLocalNames_Type
Name As String
Sheet As String
Ref As String
End Type
Sub GlobalNamesToLocalNames(Optional Void As Variant)
Dim List() As GlobalNamesToLocalNames_Type
Dim Count As Long
Dim Name As Name
Dim Dat() As String
Dim X As Long
' count the size
For Each Name In ActiveWorkbook.Names
Count = Count + 1
Next
ReDim List(Count - 1)
Count = 0
' Collecect all name data
For Each Name In ActiveWorkbook.Names
With List(Count)
' Pick up only the name
If InStr(Name.Name, "!") > 0 Then
Dat = Split(Name.Name, "!")
.Name = Dat(1)
Else
.Name = Name.Name
End If
' pick up the sheet and refer
Dat = Split(Name.RefersTo, "!")
.Sheet = Mid(Dat(0), 2)
.Ref = Dat(1)
' make local sheet name
.Name = .Sheet & "!" & .Name
End With
Count = Count + 1
Next
' Delete all names
For Each Name In ActiveWorkbook.Names
Name.Delete
Next
'rebuild all the names
For X = 0 To Count - 1
With List(X)
If Left(.Ref, 1) <> "#" Then
ActiveWorkbook.Names.Add Name:=.Name, RefersToLocal:="=" & .Sheet & "!" & .Ref
End If
End With
Next
End Sub
【讨论】:
对我来说,当我从 Name Manager 为同一范围创建 new Name tag 时,它可以让我选择更改范围;) 工作簿是默认的并且可以更改为任何可用的工作表。
【讨论】:
JS20'07'11 的代码真是不可思议的简单直接。我想提出的一个建议是在条件中加上感叹号:
InStr(1, objName.RefersTo, sWsName+"!", vbTextCompare)
因为这将防止在不正确的工作表中添加 NamedRange。例如:如果 NamedRange 引用了一个名为 Plan11 的工作表,而您有另一个名为 Plan1 的工作表,则在添加范围时,如果你不用感叹号。
更新
更正:最好使用正则表达式评估工作表的名称。您可以使用的一个简单函数如下(改编自http://blog.malcolmp.com/2010/regular-expressions-excel-add-in,启用Microsoft VBScript Regular Expressions 5.5):
Function xMatch(pattern As String, searchText As String, Optional matchIndex As Integer = 1, Optional ignoreCase As Boolean = True) As String
On Error Resume Next
Dim RegEx As New RegExp
RegEx.Global = True
RegEx.MultiLine = True
RegEx.pattern = pattern
RegEx.ignoreCase = ignoreCase
Dim matches As MatchCollection
Set matches = RegEx.Execute(searchText)
Dim i As Integer
i = 1
For Each Match In matches
If i = matchIndex Then
xMatch = Match.Value
End If
i = i + 1
Next
End Function
所以,你可以使用类似的东西:
xMatch("'?" +sWsName + "'?" + "!", objName.RefersTo, 1) <> ""
而不是
InStr(1, objName.RefersTo, sWsName+"!", vbTextCompare)
这将涵盖 Plan1 和 'Plan1'(当范围涉及多个单元格时)变体
提示:避免使用单引号 (')、:) 的工作表名称。
【讨论】:
我在 JS20'07'11 以前的 Makro 中添加了一些额外的代码行,以确保工作表的命名范围的名称不是工作簿的命名范围的名称。 如果没有这些行,已定义的工作簿铲取命名范围将被删除和替换。
Public Sub RescopeNamedRangesToWorkbookV2()
Dim wb As Workbook
Dim ws As Worksheet
Dim objNameWs As Name
Dim objNameWb As Name
Dim sWsName As String
Dim sWbName As String
Dim sRefersTo As String
Dim sObjName As String
Set wb = ActiveWorkbook
Set ws = ActiveSheet
sWsName = ws.Name
sWbName = wb.Name
'Loop through names in worksheet.
For Each objNameWs In ws.Names
'Check name is visble.
If objNameWs.Visible = True Then
'Check name refers to a range on the active sheet.
If InStr(1, objNameWs.RefersTo, sWsName, vbTextCompare) Then
sRefersTo = objNameWs.RefersTo
sObjName = objNameWs.Name
'Check name is scoped to the worksheet.
If objNameWs.Parent.Name <> sWbName Then
'Delete the current name scoped to worksheet replacing with workbook scoped name.
sObjName = Mid(sObjName, InStr(1, sObjName, "!") + 1, Len(sObjName))
'Check to see if there already is a Named Range with the same Name with the full workbook scope.
For Each objNameWb In wb.Names
If sObjName = objNameWb.Name Then
MsgBox "There is already a Named range with ""Workbook scope"" named """ + sObjName + """. Change either Named Range names or delete one before running this Macro."
Exit Sub
End If
Next objNameWb
objNameWs.Delete
wb.Names.Add Name:=sObjName, RefersTo:=sRefersTo
End If
End If
End If
Next objNameWs
End Sub
【讨论】:
在 theexceladdict.com 上找到了这个
在工作表上选择要更改其范围的命名范围;
打开名称管理器(公式选项卡)并选择名称;
点击删除并确定;
单击新建...,然后在名称字段中输入原始名称;
确保将范围设置为工作簿,然后单击关闭。
【讨论】: