Can I use VBA function to return a (dynamic) list of acceptable values into Excel's data validation?

后端 未结 6 1548
夕颜
夕颜 2020-12-19 11:28

For a given cell, I select Data/Validation and set Allow to \"List\". I now wish to set Source like so:

=rNames(REGS)

but that does not work (name not found

6条回答
  •  星月不相逢
    2020-12-19 12:13

    I think the problem is that data validation dialog only accepts the following "lists":

    • an actual list of things entered directly into the Source field

    • a literal range reference (like $Q$42:$Q$50)

    • a named formula that itself resolves to a range reference

    That last one is key - there is no way to have a VBA function just return an array that can be used for validation, even if you call it from a named formula.

    You can write a VBA function that returns a range reference, though, and call that from a named formula. This can be useful as part of the following technique that approximates the ability to do what you actually want.

    First, have an actual range somewhere that calls your arbitrary-array-returning VBA UDF. Say you had this function:

    Public Function validationList(someArg, someOtherArg)
    
        'Pretend this got calculated somehow based on the above args...
        validationList = Array("a", "b", "c")
    End Function
    

    And you called it from $Q$42:$Q$50 as an array formula. You'd get three cells with "a", "b", and "c" in them, and the rest of the cells would have #N/A errors because the returned array was smaller than the range that called the UDF. So far so good.

    Now, have another VBA UDF that returns just the "occupied" part of a range, ignoring the #N/A error cells:

    Public Function extractSeq(rng As Range)
    
        'On Error GoTo EH stuff omitted...
    
        'Also omitting validation - is range only one row or column, etc.
    
        Dim posLast As Long
        For posLast = rng.Count To 1 Step -1
            If Not IsError(rng(posLast)) Then
                Exit For
            End If
    
            If rng(posLast) <> CVErr(xlErrNA) Then
                Exit For
            End If
        Next posLast
    
        If posLast < 1 Then
            extractSeq = CVErr(xlErrRef)
        Else
            Set extractSeq = Range(rng(1), rng(posLast))
        End If
    End Function
    

    You can then call this from a named formula like so:

    =extractSeq($Q$42:$Q$50)
    

    and the named formula will return a range reference that Excel will accept an allowable validation list. Clunky, but side-effect free!

    Note the use of the keyword 'Set' in the above code. It's not clear from your question, but this might be the only part of this whole answer that matters to you. If you don't use 'Set' when trying to return a range reference, VBA will instead return the value of the range, which can't be used as a validation list.

提交回复
热议问题