4

I am looking for alternative, general coding methods for dealing with global named ranges, in VBA. I'm hoping for answers, here, with some new generalized suggestions and approaches.

I suggest a few methods I've used, but the methods don't avoid all problems -- I would like: ease of coding and spreadsheet drafting; tolerant of spreadsheet changes, and; ease of lookup/reference months later.

As I draft a spreadsheet that will later use VBA, I create named ranges (typically global names) in spreadsheet formulas. The ranges are useful there on the sheets, and useful as a reference from VBA. Typically, I do not add/change the names collection in VBA; I merely reference the collection.

When coding VBA, I access named ranges created in the workbook. If I cut/paste named cells/ranges, editing the workbook and sheets, the VBA still works.

Yet, Global Names, created in a worksheet environment, don't meet all three requirements in the VBA environment -- especially when modifying code or modifying the worksheets.

In My Perfect World:

wb.range("myGlobalRangeName")

My Perfect World hopes that a workbook's global references created on a spreadsheet are global -- but without clarification VBA expects that reference to be on the ActiveWorkBook and ActiveWorkSheet.

Thought One: I know that Range("myGlobalRangeName") accesses that range, and

Thus, I often use this fragment

wb.Worksheets("SheetOne").Range("myGlobalRangeName")

maybe constructed inside With Blocks, specifying the worksheet even though the range is a global reference.

Moving named cells to other sheets breaks this reference (even though the Name is Global!). I have to backtrack through all the code looking for misplaced references; or I can execute the code and hope to catch the errors...

Thought Two: I can write, instead, something like this, to access the name collection for the workbook:

wb.Names("myGlobalRangeName").RefersToRange

but having to append RefersToRange is .... well ... annoying. It misses the simplicity of the perfect world.

Thought Three: I create a distinct worksheet with all the values I want to trap in the other sheets, and I create ranges on that distinct sheet, only. Cell references in the workbook and accessed with VBA both work. That way, the VBA begins with

Dim wsNames as spreadsheet
set wsNames = wb.worksheets("SheetWithNames")

and the name references always used wb.wsNames, and looked like this:

with wb
    .... .wsNames.range("myGlobalRangeName") ....
end with

or other useful variations.

Yet this, too, can get messy -- I have to backtrack to see where the real data is when I later amend the spreadsheet or the VBA. Sometimes, that method works, particularly if I tenaciously name ranges on that sheet only for VBA consumption, use really memorable names, and remember the locations, and remember I did all this months later...

Conclusion: Am I missing something? Are there other, maybe easier, general coding methods other than using

  • RefersToRange with the name collection, or
  • wb.worksheets("SheetName").Range("myGlobalRange") references which explicitly identify the [current] sheet, or
  • placing RangeNames and referral formulas on a separate sheet, with VBA range references as wsNames.Range("myGlobalRange").

I don't want to type so much; or create procedure variables to trap values before using them in assignments. The mess gets...well...harder to read and worse to track if I am assigning one cell's value to another in another workbook, and one or both use global ranges.

Mikku
  • 6,538
  • 3
  • 15
  • 38
John
  • 207
  • 3
  • 12
  • When creating the named range, are you scoping this to a specific sheet, or are you leaving the default scope set as Workbook? Perhaps defining a scope to the Sheet you are interested would be better? If that doesn't work, maybe just write a function to return the range for the specific Worbook/Sheet combo? – Ryan Wildry Jan 04 '19 at 19:19
  • @RyanWildry: I use global names. And, in any case, would I not have to make sheet references within VBA? I rely on the spreadsheet to mock up input, columnar tables, answer tabs, and more, and then use VBA in the background to process. This way, I use native Excel handi-ness to do all the background work. What I'm looking at with this question is more at a way to code, efficiently, and still address my initial goals: ease of coding, tolerant of spreadsheet changes, ease of lookup/reference months later – John Jan 09 '19 at 04:19
  • You can set references to named ranges, specific to a sheet when creating them. Would that help? – Ryan Wildry Jan 09 '19 at 13:54
  • @RyanWildry -- If I understand you correctly...that's what I do. I create named ranges in the sheets, as I draft the sheet. E.g., "name_dateRangeStart", for a cell that holds that specific piece of data I know I will want to address in the VBA code. If/when I move the cell for formatting or other purposes, that "storage location" is still accessible. – John Jan 09 '19 at 15:35
  • *VBA expects that reference to be on the ActiveWorkBook and ActiveWorkSheet* And where did you get this? Named range can have two type of visibility: worksheet and workbook. – JohnyL Jan 09 '19 at 17:29
  • Anyway, it's not clear what you're trying to achieve. – JohnyL Jan 09 '19 at 17:30
  • @JohnyL ... the expectation is, I think, for when one types, only: Range("MyName"), without further qualification. So you always have to point to some sheet as worksheet("sheetName")....and I've noticed that even though the name is global, in VBA you still have to point to the specific sheet where the name points to. – John Jan 12 '19 at 00:30
  • @John Could we contact through Skype to discuss further? My Skype: sekktor81 – JohnyL Jan 12 '19 at 07:42
  • I have never seen this behavior and I started with Excel 95. I use code like this:```set rng = Range("globalname")``` for a range reference, or ```v = Range("globalname")``` to fetch a cell value, and it works in any type of module. The only caveat is that code in one sheet module sometimes cannot access values in another sheet. – Tom the Toolman Jan 03 '20 at 19:09

2 Answers2

0

I've generally gotten what I needed with the Workbook Names collection, which can store formulas and ranges. Both can be either relative to the worksheet that is active, or absolute.

The big difference is between:

Call Thisworkbook.Names.Add(Name:="Bob", RefersTo:=Range("Sheet2!$F$19"))

and

Call Thisworkbook.Names.Add(Name:="Doug", RefersTo:="Sheet2!$F$19")

If that Cell F19 on Sheet2 is moved, then Bob will refer to the new location, anywhere in the workbook; move it to new sheets, Bob will follow along. But Doug will refer to Sheet2, F19, forever, no matter what.

The reference then is just [Bob] or Range("Bob") and will refer to another worksheet than the activesheet if need be.

StackExchange noted a related question that was very interesting:

Excel VBA: Workbook-scoped, worksheet dependent named formula/named range (result changes depending on the active worksheet)

...this is where a workbook-level name can hold a formula, and the formula can either have one of its values be the same through the workbook, or relate to a local value, per sheet. And indeed, you can mix the two in one formula.

Roy Brander
  • 121
  • 6
  • An interesting comment, but I don't seek to add/edit the names collection with VBA, more at merely seeking simple means to make the name reference in VBA and still meet my basic goals. The 'bang' notation within your URL referenced does present dangers, and as an aside might be more familiar in the access VBA context. Using the bang notation for my purposes defeats my goals, as I want to avoid references to specific sheets. Other aside...does the bang refer to the default property, rather than 'active sheet'? I seem to recall a string of defaults that makes it behave as activesheet...? – John Jan 09 '19 at 04:25
  • Sorry I couldn't help you! You did say "I am looking for alternative, general coding methods for dealing with global named ranges" so it's unfortunate you don't "seek to add/edit the names collection", since that is certainly an alternative general coding method. I'm not sure how the bang notation is "dangerous", it's just a tool; hammers are dangerous to thumbs if not used carefully. I'm not sure how to avoid references to "specific sheets" ONCE and still reference data at all. The names approach limits the specific-sheet reference to ONE location. All others are to the name alone. – Roy Brander Jan 10 '19 at 17:00
  • Your comment worthy. I meant: I don't, in the course of coding, normally add/edit names. But I see a method, sort of like the one in the answer below, a combination of yours and his....same idea? – John Jan 12 '19 at 00:38
0

You could store your name ranges into an Enum, then wrap a class around accessing them. It would be a bit of work setting up, however, you could automate the creation of this class which would be specific to an existing workbook with some VBA IDE programming if you want.

I didn't do that part, but I'm sharing the approach that might help.

Add this to a Class, name this NameRangeHelper

'Update these to correspond to the named ranges in your workbook, or
'those range you want access to with this approach
Public Enum NamedRanges
    Example1
    Example2
    Example3
End Enum

Private pNamedRanges As Object

Private Sub Class_Initialize()
    Dim NamedRange  As Name
    Dim NamedRanges As Names

    Set NamedRanges = ThisWorkbook.Names
    Set pNamedRanges = CreateObject("Scripting.Dictionary")

    For Each NamedRange In NamedRanges
        If Not pNamedRanges.Exists(NamedRange.Name) Then
            If TypeName(NamedRange.RefersToRange) = "Range" Then pNamedRanges.Add NamedRange.Name, NamedRange.RefersToRange
        End If
    Next

End Sub

Private Sub Class_Terminate()
    Set pNamedRanges = Nothing
End Sub

Public Function GetRange(RangeName As NamedRanges) As Excel.Range
    Dim RangeStringName As String
    RangeStringName = GetEnumName(RangeName)
    If pNamedRanges.Exists(RangeStringName) Then Set GetRange = pNamedRanges(RangeStringName)
End Function

Private Function GetEnumName(RangeName As NamedRanges) As String
    Select Case RangeName
        Case NamedRanges.Example1
            GetEnumName = "Example1"
        Case NamedRanges.Example2
            GetEnumName = "Example2"
        Case NamedRanges.Example3
            GetEnumName = "Example3"
        Case Else
            GetEnumName = vbNullString
    End Select
End Function

Public Function GetSheetFromRange(RangeName As NamedRanges) As Excel.Worksheet
    Set GetSheetFromRange = GetRange(RangeName).Parent
End Function

Public Function GetWorkbookFromRange(RangeName As NamedRanges) As Excel.Workbook
    Set GetWorkbookFromRange = GetRange(RangeName).Parent.Parent
End Function

Here is the client code, with an example of accessing a Range with a defined Enum.

 Sub ExampleNamedRangeHelper()
    Dim rngHelper As NamedRangeHelper: Set rngHelper = New NamedRangeHelper
    Dim rng       As Range
    Set rng = rngHelper.GetRange(Example1)
    Debug.Print rng.Address, rngHelper.GetSheetFromRange(Example1).Name, rngHelper.GetWorkbookFromRange(Example1).Name
End Sub

Using this approach, your names are in an Enum and you get the Intellisense to make picking/remembering the range names easier.

Ryan Wildry
  • 5,612
  • 1
  • 15
  • 35
  • That's an interesting method. When you say the IDE.....that's the VBE IDE ? This works, for me, but if I send the spreadsheet to others, they have to have the priviledge to open up that on the Options/Trust, and know how to do that. Those steps present a problem.... but I like the idea. Looks like it may address my original goals, I'm going to read through it later, carefully, to understand the coding. Aside: I need to add another "goal"....ports to users/clients who don't need to fool with trust and options.... – John Jan 12 '19 at 00:33
  • Hmmm. I need to not only name on the spreadsheet, but enum in VBA. Could that later step be automated....? Or am I missing something? I would like to avoid having to type things twice. Once on the worksheet [name] and then references in VBA. On yours do I have to enum the thing correctly, and then more changes there with changes on the spreadsheet name textcharacters chosen? Seems like one could automate the lookup....? Reads in theory like what RoyBrander, above, suggested? – John Jan 12 '19 at 00:36
  • Hi John. That's the VBA IDE automation piece I was referring to. Not sure of your workflow specifics, but this is something you can automate, then distribute the file. Here's an example I wrote a few days ago for doing some VBA IDE automation, you will need to Trust the VBA object model. Here's a link (look at the second example): https://stackoverflow.com/a/54130214/4839827 – Ryan Wildry Jan 12 '19 at 00:43
  • I believe I addressed your question about the approach, the code creation automation seemed separate, but happy to help if another question is created. – Ryan Wildry Jan 12 '19 at 00:45
  • If I distribute my work to a client , do they have to trust the VBA Object Model? I was assuming, yes, and therefore can't send the code to an agency or company which does not allow that. – John Jan 12 '19 at 22:44
  • Again, this depends on your workflow. I would suspect not. You'd dynamically create this code, test it, then send to the client. Only you'd need to trust the VBA object model to create code, once that is done, unless the client needs to do the same code creation activity, you should be fine. If you are sending a finished product to a client to use, the answer is a fairly definitive NO. – Ryan Wildry Jan 13 '19 at 13:47
  • I am perplexed. The VBE IDE requires a specific check box in the options/Trust to work. I thought. If not check, there on a client machine, and if the user/client doesn't know how to check the box, or the organization prohibits checking, then what is the alternative.? Distiguish this from other VBA references, for which one can choose late/early binding? On some research, see this url: https://support.microsoft.com/en-in/help/282830/programmatic-access-to-office-vba-project-is-denied – John Jan 15 '19 at 23:56
  • Hi John. Are the clients machines adjusting the code you provide? If the answer is yes, then that option must be enabled, if no, that option only needs to be enabled for you. – Ryan Wildry Jan 16 '19 at 01:08
  • The clients wouldn't be changing code, more than likely. A recent experience with a client's use of my code failed, and was cured by enabling that specific option. That started me thinking, and drives my concern here. Your answer is the opposite, and contrary to the i-net readings referred to above. ---> Unless....I am missing something about how to make a reference to the VBE and get around the checkbox -- I used something like this within my code's error recovery, if I remember right: `Application.VBE.ActiveCodePane.CodeModule` – John Jan 23 '19 at 01:28
  • If they aren’t changing code, this approach works. Just create the dynamically, then ship to the client. That option only needs to be checked for the code creation activity. That’s it. – Ryan Wildry Jan 23 '19 at 03:27