Получение именованного диапазона по строке в книге Excel при дублировании имени

Чтобы уточнить, скажем, у меня есть два именованных диапазона в моей книге. Оба именованных диапазона имеют одно и то же имя (скажем, «myName»), но один относится к Sheet1, а другой - к Workbook.

Учитывая имя (строку) именованного диапазона, я хочу получить именованный диапазон уровня рабочей книги.

Если я использую собственный вызов: wb.Names.Item("myName"), он возвращает именованный диапазон в области видимости листа.

Если вместо этого я сделаю: wb.Names.Item("Sheet1!myName"), это, очевидно, вернет диапазон имен листа. Я обнаружил, что могу использовать это, чтобы указать конкретные листы, но не книгу.

Есть ли способ указать, что я хочу, чтобы рабочая книга была ограничена областью?

В настоящее время я перебираю список всех имен и сравниваю свойство .Name, чтобы получить именованный диапазон области книги. Это работает, потому что свойство .Name добавляет "Sheet1!" в именованный диапазон с ограниченной областью листа. Однако это очень затратно, и я хочу избежать этого.


person Shark    schedule 09.10.2012    source источник
comment
Мне не на 100% понятно, как вы используете диапазон. Если вы пытались установить объект диапазона в строку имени, тогда Set rng1 = Range("myName") вернет диапазон из имени локального листа, если лист1 активен, иначе он вернет диапазон из имени книги. Я думаю, что это самый простой обходной путь - то есть проверьте, что активный лист отличается от листа, на котором размещено локальное имя, прежде чем использовать это имя в качестве области книги. Есть смысл? :)   -  person brettdj    schedule 10.10.2012
comment
вы также можете сравнить parent.name с именем книги, но вам все равно нужно перебирать имена, чтобы извлечь эту коллекцию.   -  person nutsch    schedule 10.10.2012
comment
re: комментарий @ brettdj. в зависимости от того, чего вы пытаетесь достичь, самым простым может быть просто добавить временный лист в начале вашего макроса, который будет удален в конце вашего кода.   -  person nutsch    schedule 10.10.2012
comment
Нет, неважно, на каком ты листе. Сначала он возвращает уровень листа (если существует несколько листов с ограниченным диапазоном, он упорядочивает их на основе порядка листов). Если файлов с областью действия листа не существует, возвращается объект с областью видимости wb. И я не пытаюсь установить диапазон. Я просто хочу заполучить COM-объект, чтобы делать с ним все, что мне нужно.   -  person Shark    schedule 10.10.2012
comment
@nutsch Я стараюсь не повторять все имена, так как это дорогостоящая вещь. Я понимаю, что могу использовать и родительское свойство. Я считаю, что ответ был здесь: stackoverflow.com/questions/8656793/   -  person Shark    schedule 10.10.2012
comment
@Shark Не имеет значения, на каком листе вы находитесь, если вы устанавливаете объект диапазона в соответствии с моим образцом кода.   -  person brettdj    schedule 10.10.2012
comment
@brettdj Понятно. Вы получаете диапазон, связанный с именем. Однако это вернет объект Range, а не объект NamedRange. Затем вы сталкиваетесь с проблемой извлечения NamedRange из диапазона, которая имеет свои собственные проблемы. (Ваш синтаксис vba тоже меня отбросил, я должен указать, что работаю с C #)   -  person Shark    schedule 10.10.2012
comment
@Shark, можете ли вы провести однократный анализ всех имен в книге, а затем сохранить именованные диапазоны в области видимости книги в словаре? Похоже, что такой тип кеширования снизит любые проблемы с производительностью.   -  person devuxer    schedule 10.10.2012
comment
@Shark, ты действительно уверен, что у тебя проблема? Убедитесь, что wb установлен для книги, а не для листа.   -  person Robert Co    schedule 10.10.2012
comment
Насколько мне известно, ваш обходной путь - единственный способ решить эту проблему.   -  person Chris Rae    schedule 30.11.2012


Ответы (1)


Когда мы (JKP и я) писали Name Manager, мы специально добавляли фильтр и предупреждающее сообщение для Duplicate. Глобальные / локальные имена, потому что такое поведение объектной модели Excel, о котором вы упоминаете, затрудняет обнаружение ошибок.

Поэтому я рекомендую никогда не использовать повторяющиеся глобальные / локальные имена.

Мы используем код, чтобы определить, если имя дублируется глобальное / локальное с активным родительским элементом локального имени, а затем при необходимости переключайте листы. Оптимизированный код VBA, который мы используем для поиска локальной версии глобального имени, таков: он достаточно быстрый, если у вас нет нескольких десятков тысяч имен -

    Function FindNameLocal(oSheet As Worksheet, sName As String) As Name
        Dim oName As Name
        Dim strLocalName As String
        Dim strLocalNameNoQuote
        Dim strName As String
        Set FindNameLocal = Nothing
        If Len(sName) > 0 And Not oSheet Is Nothing And oSheet.Names.Count > 0 Then
            On Error Resume Next
            strLocalName = "'" & oSheet.Name & "'!" & sName
            strLocalNameNoQuote = oSheet.Name & "!" & sName
            Set FindNameLocal = oSheet.Names(strLocalName)
            If Err <> 0 Or (FindNameLocal.NameLocal <> strLocalName And FindNameLocal.NameLocal <> strLocalNameNoQuote) Then
                On Error GoTo 0
                Set FindNameLocal = Nothing
                For Each oName In oSheet.Names
                    strName = oName.Name
                    If Len(strLocalName) = Len(strName) Or Len(strLocalNameNoQuote) = Len(strName) Then
                        If strName = strLocalName Or strName = strLocalNameNoQuote Then
                            Set FindNameLocal = oName
                            GoTo GoExit
                        End If
                    End If
                Next
            End If
        End If
GoExit:
    End Function
person Charles Williams    schedule 10.10.2012
comment
Я не уверен, что это ответ на мой вопрос. Возможно, я неправильно это читаю, но мне нужно получить глобальную версию имени. Это возвращает первый уровень листа, что я уже могу сделать, передав его в формате wb.Names.Item (Sheet1! MyName). Я заметил одну вещь в вашем коде, которая мне любопытна. Вызывает ли сравнение длины перед сравнением строк значительный прирост производительности? - person Shark; 15.10.2012
comment
Мы используем это, чтобы определить, есть ли дублированное глобально-локальное имя, передавая глобальное имя и имя активного рабочего листа. Если есть дубликат, вам нужно изменить рабочий лист и повторить попытку, пока вы не найдете рабочий лист, который не содержит дубликата (или временно добавите новый лист), чтобы затем, когда вы получите доступ к имени, вы получите глобальное имя. Получение длины строки в VBA выполняется очень быстро, но сравнение строк происходит медленно: не знаю, в какой степени это применимо в C #. - person Charles Williams; 15.10.2012
comment
Ах, теперь имеет смысл. Мне нужно будет посмотреть, будет ли ваш подход (переведенный на C #) быстрее, чем другая идея, которую я придумал. Я отправлю решение в качестве другого ответа. Тогда тестирование производительности определит победителя! - person Shark; 16.10.2012
comment
Кстати, если вы работаете на американском английском, вы можете пропустить цикл For Each. - person Charles Williams; 16.10.2012