Хитрости »
Основные понятия (23)
Сводные таблицы и анализ данных (9)
Графики и диаграммы (5)
Работа с VB проектом (12)
Power BI и Power Query (14)
Условное форматирование (5)
Списки и диапазоны (5)
Макросы(VBA процедуры) (63)
Разное (38)
Баги и глюки Excel (2)

Кто вызвал функцию или процедуру?

Предположим вы написали два макроса - один скрывает строки, другой отображает.

'скрываем строки
Sub HideRows()
    Range("A3:A14").EntireRow.Hidden = True
End Sub
'показываем строки
Sub UnhideRows()
    Range("A3:A14").EntireRow.Hidden = False
End Sub

И, конечно - создали кнопки для вызова этих двух кодов (подробнее про создание кнопок - Как создать кнопку для вызова макроса на листе). Но потом захотелось большего - чтобы была всего одна кнопка и первым нажатием строки скрывались, а вторым отображались. Сделать это не проблема, если применить такой финт:

Sub HideUnhideRows()
    Range("A3:A14").EntireRow.Hidden = Not Range("A3:A14").EntireRow.Hidden
End Sub

Но если код делается для пользователей, то лучше как-то дать понять им, в каком состоянии сейчас строки - скрыты или отображены и к какому действию приведет нажатие на кнопку. И лучше всего это сделать надписью на самой кнопке. Если кнопка одна на одном листе, то проблем быть не должно:

Sub HideUnhideRows()
    If Range("A3:A14").EntireRow.Hidden Then
        Range("A3:A14").EntireRow.Hidden = False
        ActiveSheet.Shapes(1).TextFrame2.TextRange.Text = "Скрыть строки"
    Else
        Range("A3:A14").EntireRow.Hidden = True
        ActiveSheet.Shapes(1).TextFrame2.TextRange.Text = "Показать строки"
    End If
End Sub

Но если в книге несколько листов и на каждом по несколько кнопок, то не очень удобно будет в коде макроса указывать нужную кнопку. Да, можно указать по имени фигуры: ActiveSheet.Shapes("Скругленный прямоугольник 1"). Но опять же - если кнопок много придется давать той единственной свое уникальное имя, совпадающее на всех листах. Но можно сделать проще - использовать свойство Caller:

Sub HideUnhideRows()
    Dim sShName As String
    sShName = Application.Caller
    If ActiveSheet.Shapes(sShName).TextFrame2.TextRange.Text = "Показать строки" Then
        Range("A3:A14").EntireRow.Hidden = False
        ActiveSheet.Shapes(sShName).TextFrame2.TextRange.Text = "Скрыть строки"
    Else
        Range("A3:A14").EntireRow.Hidden = True
        ActiveSheet.Shapes(sShName).TextFrame2.TextRange.Text = "Показать строки"
    End If
End Sub

Скачать пример:

  Автоопределение нажатой кнопки.xls (57,5 KiB, 79 скачиваний)


Теперь рассмотрим другую ситуацию, более распространенную. Вы написали свою пользовательскую функцию, которая должна суммировать данные ячейки со всех листов книги, кроме того, в котором сама функция. Часто это делают так:

Function СуммаЯчеекВсехЛистов(Ячейка As Range)
    Dim ws As Worksheet 'объявляем переменную для обращения к листам в цикле
    Dim dblSum As Double 'переменная для хранения суммы
    'цикл по листам книги
    For Each ws In ActiveWorkbook.Worksheets
        If Not ws Is ActiveSheet Then 'исключаем активный лист из суммирования
            dblSum = dblSum + ws.Range(Ячейка.Address).Value
        End If
    Next ws
    'присваиваем значение суммы функции
    СуммаЯчеекВсехЛистов = dblSum
End Function

Но это очень неправильно. Во-первых, цикл идет по листам активной книги. А это значит, что если с этой книги перейти в другую - то функция будет вычислять сумму на листах именно этой книги, а не той, в которой записана функция. Во-вторых, строка If Not ws Is ActiveSheet Then исключает из суммирования лист активной книги, а не той книги, в которой записана функция. Это может привести к ошибочным расчетам, что весьма критично, если на расчеты функции опираются функции других книг и листов. Поэтому надо определять не активную книгу, а именно ту, в которой функция. Здесь опять поможет свойство Caller:

Function СуммаЯчеекВсехЛистов(Ячейка As Range)
    Dim ws As Worksheet     'объявляем переменную для обращения к листам в цикле
    Dim dblSum As Double    'переменная для хранения суммы
    Dim rFuncCell As Range  'переменная для хранения ссылки на ячейку с функцией
    Dim wsFunc As Worksheet 'переменная для хранения ссылки на лист с функцией
    Dim wbFunc As Workbook  'переменная для хранения ссылки на книгу с функцией
 
    Set rFuncCell = Application.Caller 'ячейка с функцией
    Set wsFunc = rFuncCell.Parent      'лист с функцией
    Set wbFunc = wsFunc.Parent         'книга с функцией
    'для листа и книги можно записать одной строкой:
    'Set wsFunc = Application.Caller.Parent        'лист с функцией
    'Set wbFunc = Application.Caller.Parent.Parent 'книга с функцией
 
    'цикл по листам книги с функцией
    For Each ws In wbFunc.Worksheets
        If Not ws Is wsFunc Then 'исключаем лист с функцией из суммирования
            dblSum = dblSum + ws.Range(Ячейка.Address).Value
        End If
    Next ws
    'присваиваем значение суммы функции
    СуммаЯчеекВсехЛистов = dblSum
End Function

А теперь попробуем разобраться, что же за зверь такой, этот Caller.
Caller - свойство объекта Application, которое возвращает информацию о том, как(чем) был вызван код. Есть несколько вариантов вызова и в зависимости от них значение, возвращаемое Caller меняется:

  • Если вызов был из функции пользователя - Caller вернет объект Range, представляющий ссылку на ячейку, в которой записана функция пользователя. Если это функция введена как формула массива - то Caller вернет ссылку на все ячейки, в которые записана функция
  • Если вызов был кнопкой на листе - Caller вернет текст, содержащий локальное имя объекта Shape, к которому привязан вызов процедуры
  • Если вызов был из событийной процедуры(Workbook_Open и им подобные), либо процедура была вызвана через Alt+F11 - Caller вернет ошибку REF
  • Если процедура вызвана с панели(Ribbon или настраиваемая панель) - Caller будет иметь тип Variant(), но не сможет определить как именно был вызван код и при попытке обращения к нему получим так же ошибку REF
  • Если вызов был через процедуры автоматизации (Auto_Open, Auto_Close, Auto_Activate, Auto_Deactivate) - Caller вернет тип String и содержит имя книги и активного листаэто устаревшие процедуры, которые сейчас заменены событийными в классах книг и листов, но тем не менее их можно встретить в некоторых кодах

Для примера можно скачать файл по ссылке:

  Caller test.xls (56,0 KiB, 71 скачиваний)


Там записаны две функции - одна с использованием ссылки на активную ячейку, другая с применением Caller:

'возвращает адрес активной ячейки активного листа, даже если активная ячейка в другой книге
Function GetActCell()
    Application.Volatile True
    GetActCell = "'GetActCell' return address: " & ActiveCell.Address(0, 0, xlA1, True)
End Function
'возвращает всегда адрес той ячейки, в которой записана
Function GetActCell_Caller()
    Application.Volatile True
    GetActCell_Caller = "'GetActCell_Caller' return address: " & Application.Caller.Address(0, 0, xlA1, True)
End Function

Чтобы во всей мере понять смысл объекта Caller поизменяйте значения любых ячеек на листе с функциями. Потом перейдите на любой другой лист этой же книги - измените там какую-нибудь ячейку. Вернитесь на лист с функциями и посмотрите, что там отображается. Так же можно перейти в другую книгу и изменить что-то там. Вернуться и посмотреть результат. В ячейке с GetActCell будет отображаться адрес той ячейки, которая была изменена последней. В ячейке же с GetActCell_Caller всегда будет адрес именно той ячейки, в которой записана сама функция GetActCell_Caller.

Хотел бы так же отметить, что для определения ячейки с функцией можно использовать объект Application.ThisCell, который возвращает ссылку на ячейку, из которой была вызвана функция. Этот объект внутри функций пользователя можно применять точно так же, как и Caller. Но он не может быть применен для определения других методов вызова функций и процедур, как Caller.


Статья помогла? Поделись ссылкой с друзьями!
  Плейлист   Видеоуроки

Поиск по меткам

Access apple watch Multex Outlook Power Query и Power BI VBA работа в редакторе VBA управление кодами Бесплатные надстройки Дата и время Диаграммы и графики Записки Защита данных Интернет Картинки и объекты Листы и книги Макросы и VBA Надстройки Настройка Печать Поиск данных Политика Конфиденциальности Почта Программы Работа с приложениями Работа с файлами Разработка приложений Сводные таблицы Списки Тренинги и вебинары Финансовые Форматирование Формулы и функции Функции Excel Функции VBA Ячейки и диапазоны акции MulTEx анализ данных баги и глюки в Excel ссылки
Поделитесь своим мнением

Комментарии, не имеющие отношения к комментируемой статье, могут быть удалены без уведомления и объяснения причин. Если есть вопрос по личной проблеме - добро пожаловать на Форум


Для оформления сообщений Вы можете использовать следующие тэги:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Тренинги

Заказать
Юридическая информация

Использование материалов сайта

Политика Конфиденциальности

ИП Щербаков Дмитрий Валентинович
ОГРНИП: 318502700083307
ИНН: 504013350772

Наши партнеры

Перейти
Перейти

Счетчики

Рейтинг@Mail.ru Яндекс.Метрика
© 2018 Excel для всех   Войти