Создание пользовательской функции в VBA Excel, ее синтаксис и компоненты. Описание пользовательской функции и ее аргументов. Метод Application.MacroOptions.
Пользовательская функция — это процедура VBA, которая производит заданные вычисления и возвращает полученный результат. Используется для вставки в ячейки рабочего листа Excel или для вызова из других процедур.
Объявление пользовательской функции
Синтаксис функции
1 2 3 4 5 6 7 |
[Static] Function Имя ([СписокАргументов])[As ТипДанных] [Операторы] [Имя = выражение] [Exit Function] [Операторы] [Имя = выражение] End Function |
Компоненты функции
- Static — необязательное ключевое слово, указывающее на то, что значения переменных, объявленных в функции, сохраняются между ее вызовами.
- Имя — обязательный компонент, имя пользовательской функции.
- СписокАргументов — необязательный компонент, одна или более переменных, представляющих аргументы, которые передаются в функцию. Аргументы заключаются в скобки и разделяются между собой запятыми.
- Операторы — необязательный компонент, блок операторов (инструкций).
- Имя = выражение — необязательный* компонент, присвоение имени функции значения выражения или переменной. Обычно, значение присваивается функции непосредственно перед выходом из нее.
- Exit Function — необязательный компонент, принудительный выход из функции, если ей уже присвоено окончательное значение.
*Один из компонентов Имя = выражение следует считать обязательным, так как если не присвоить функции значения, смысл ее использования теряется.
Видимость функции
Видимость пользовательской функции определяется необязательными ключевыми словами Public и Private, которые могут быть указаны перед оператором Function (или Static, в случае его использования).
Ключевое слово Public указывает на то, что функция будет доступна для вызова из других процедур во всех модулях открытых книг Excel. Функция, объявленная как Public, отображается в диалоговом окне Мастера функций.
Ключевое слово Private указывает на то, что функция будет доступна для вызова из других процедур только в пределах программного модуля, в котором она находится. Функция, объявленная как Private, не отображается в диалоговом окне Мастера функций, но ее можно ввести в ячейку вручную.
Если ключевое слово Public или Private не указано, функция считается по умолчанию объявленной, как Public.
Чтобы пользовательская функция всегда была доступна во всех открытых книгах Excel, сохраните ее в Личной книге макросов без объявления видимости или как Public. Но если вы планируете передать рабочую книгу с пользовательской функцией на другой компьютер, код функции должен быть в программном модуле передаваемой книги.
Пример пользовательской функции
Для примера мы рассмотрим простейшую пользовательскую функцию, которой в следующем параграфе добавим описание. Называется функция «Деление», объявлена с типом данных Variant, так как ее возвращаемое значение может быть и числом, и текстом. Аргументы функции — Делимое и Делитель — тоже объявлены как Variant, так как в ячейках Excel могут быть числовые значения разных типов, и функция IsNumeric тоже проверяет разные типы данных и требует, чтобы ее аргументы были объявлены как Variant.
1 2 3 4 5 6 7 8 9 10 11 |
Function Деление(Делимое As Variant, Делитель As Variant) As Variant If IsNumeric(Делимое) = False Or IsNumeric(Делитель) = False Then Деление = "Ошибка: Делимое и Делитель должны быть числами!" Exit Function ElseIf Делитель = 0 Then Деление = "Ошибка: деление на ноль!" Exit Function Else Деление = Делимое / Делитель End If End Function |
Эта функция выполняет деление значений двух ячеек рабочего листа Excel. Перед делением проверяются два блока условий:
- Если делимое или делитель не являются числом, функция возвращает значение: «Ошибка: Делимое и Делитель должны быть числами!», и производится принудительный выход из функции оператором Exit Function.
- Если делитель равен нулю, функция возвращает значение: «Ошибка: деление на ноль!», и производится принудительный выход из функции оператором Exit Function.
Если проверяемые условия не выполняются (возвращают значение False) производится деление чисел и функция возвращает частное (результат деления).
Вы можете скопировать к себе в стандартный модуль эту функцию и она станет доступна в разделе «Определенные пользователем» Мастера функций. Попробуйте вставить функцию «Деление» в ячейку рабочего листа с помощью Мастера и поэкспериментируйте с ней.
Практического смысла функция «Деление» не имеет, но она хорошо демонстрирует как объявляются, создаются и работают пользовательские функции в VBA Excel. А еще она поможет продемонстрировать, как добавлять к функциям и аргументам описания. С полноценной пользовательской функцией вы можете ознакомиться здесь.
Добавление описания функции
В списке функций, выводимом Мастером, невозможно добавить или отредактировать их описание. Список макросов позволяет добавлять процедурам описание, но в нем нет функций. Проблема решается следующим образом:
- Запустите Мастер функций, посмотрите, как отображается имя нужной функции и закройте его.
- Откройте список макросов и в поле «Имя макроса» впишите имя пользовательской функции.
- Нажмите кнопку «Параметры» и в открывшемся окне добавьте или отредактируйте описание.
- Нажмите кнопку «OK», затем в окне списка макросов — «Отмена». Описание готово!
Добавление описания на примере функции «Деление»:
Описание функции «Деление» в диалоговом окне Мастера функций «Аргументы функции»:
С помощью окна «Список макросов» можно добавить описание самой функции, а ее аргументам нельзя. Но это можно сделать, используя метод Application.MacroOptions.
Метод Application.MacroOptions
Метод Application.MacroOptions позволяет добавить пользовательской функции описание, назначить сочетание клавиш, указать категорию, добавить описания аргументов и добавить или изменить другие параметры. Давайте рассмотрим возможности этого метода, используемые чаще всего.
Пример кода с методом Application.MacroOptions:
1 2 3 4 5 6 7 |
Sub ИмяПодпрограммы() Application.MacroOptions _ Macro:="ИмяФункции", _ Description:="Описание функции", _ Category:="Название категории", _ ArgumentDescriptions:=Array("Описание 1", "Описание 2", "Описание 3", ...) End Sub |
- ИмяПодпрограммы — любое уникальное имя, подходящее для наименования процедур.
- ИмяФункции — имя функции, параметры которой добавляются или изменяются.
- Описание функции — описание функции, которое добавляется или изменяется.
- Название категории — название категории в которую будет помещена функция. Если параметр Category отсутствует, пользовательская функция будет записана в раздел по умолчанию — «Определенные пользователем». Если указанное Название категории соответствует одному из названий стандартного списка, функция будет записана в него. Если такого Названия категории нет в списке, будет создан новый раздел с этим названием и функция будет помещена в него.
- «Описание 1», «Описание 2», «Описание 3», … — описания аргументов в том порядке, как они расположены в объявлении пользовательской функции.
Эта подпрограмма запускается один раз, после чего ее можно удалить или использовать как шаблон для корректировки параметров других пользовательских функций.
Сейчас с помощью метода Application.MacroOptions попробуем изменить описание пользовательской функции «Деление» и добавить описания аргументов.
1 2 3 4 5 6 |
Sub ИзменениеОписания() Application.MacroOptions _ Macro:="Деление", _ Description:="Описание функции Деление изменено методом Application.MacroOptions", _ ArgumentDescriptions:=Array("- любое числовое значение", "- числовое значение, кроме нуля") End Sub |
После однократного запуска этой подпрограммы получаем следующий результат:
Метод Application.MacroOptions не работает в Личной книге макросов, но и здесь можно найти решение. Добавьте описания к пользовательским функциям и их аргументам в обычной книге Excel, затем экспортируйте модуль с функциями в любой каталог на жестком диске и оттуда импортируйте в Личную книгу макросов. Все описания сохранятся.
Я по-другому написал процедуру. Все-таки возникает одна и та же ошибка.
Получается так, что в процедуре есть ячейки, в которых есть формулы, которые используют пользовательскую функцию.
Функция нормально работает и вставляет формулу в нужные ячейки. Функция ссылается на два диапазона ячеек. Когда в последующей процедуре добавляются в пустые ячейки этих диапазонов значения, формула в ячейке вместо числа выдает #ЗНАЧ, пишет, что несоответствующий тип данных и, естественно нарушает все последующие действия в процедуре.
Я попробовал вставлять в ячейки значения =СLng(i), как вы и говорили, но это не помогает. Да и это происходит при пошаговой проверке через F8. Когда это в цикле происходит, то выдает какую-то другую ошибку.Когда же через F8, то формула в ячейке меняет значение с числа на #ЗНАЧ(это происходит именно в момент вставки числа в пустую ячейку из диапазона), а в процедуре перескакивает на выполнение функции и начинает многократно ее выполнять и заполняет и остальные значения с формулами величинами #ЗНАЧ. Как еще с этим можно бороться? Когда вручную вставляешь значения в пустые ячейки, то ничего не происходит, машина правильно пересчитывает эту функцию
Функция не сложная — должна работать. Попробуйте преобразовать типы данных ячеек в текстовые:
Она в целом и работает. Странный глюк возникает только при пошаговом проходе.
Но вообще большое спасибо!
Вы мне здорово помогли, особенно с адресацией ячеек в пользовательской функции. Я уже почти дописал программу.
я еще заметил, что такое возникает при зацикливании программы. Причем даже если цикл никак не связан с пересчетом значений в пользовательской функции. Может такое быть именно с этим связано?
Александр, у меня некоторые процедуры с циклами, запущенные из редактора VBA, начинают выполняться очень долго (сначала думал зависают, прерывал Ctrl+Break), хотя при нажатии на кнопку на рабочем листе выполняются моментально. Заметил это после перехода на 64-разрядную версию. Может быть и у вас нечто подобное.
У меня еще такой вопрос если можно.
А где-то есть список математических функций VBA, чтобы не пользоваться встроенными функциями эксела? Там, например, среднее, минимум, максимум, корень, логарифм и т.д.?
Без встроенных математических функций рабочего листа (WorksheetFunction) не обойтись, так как в VBA их очень мало.
Здравствуйте,
Не вижу что бы кто то в комментарии писал , поэтому вопрос
После добавления комментариев к функции Делимое — не закомментирован , почему ?
В моем тестовом варианте не воспринимает наоборот делитель
Сделал пару функций для личных нужд, но вдруг кому из читающих пригодятся. Если же умные люди найдут, что их можно оптимизировать, только спасибо.
У нас в организации графики делают с цветовым обозначением смен. Разных смен много, цветов много. А хотелось бы автоматизировать подсчет значений по сменам.
Первая функция просто вытаскивает код цвета из ячейки. Она скорее утилитарное значение имеет (не нашел аналогов в стандартных)
Вторая подсчитывает сумму ячеек с определенным цветом, и через запятую — сумму значений по этим ячейкам. В зависимости от параметра СчитатьНепустые, учитывает или не учитывает при подсчете, которые не содержат численных значений.
А в целом спасибо за ваш ресурс, очень помогает в саморазвитии )
Извиняюсь сразу, если глупый вопрос, но искал несколько раз и так и не нашел ответ.
При добавлении функции в личную книгу макросов я могу её вызвать через «Вставить функцию» (Insert Function), она всегда вставляется как Personal.xlsb!Имя функции.
Если я правильно понял, если сделано по уму, то функцию можно набирать без personal.xlsb. А как это сделать по уму — нигде не могу найти и понять сам…
Добрый день, Sergey!
Если функция размещена в личной книге макросов, имя книги (Personal.xlsb) должно быть указано, иначе Excel будет искать эту функцию в текущей книге, не найдет и возвратит ошибку «#ИМЯ?».
Спасибо, Евгений.
А есть возможность макросом записывать функцию в открытую книгу, чтобы каждый раз через алт+ф11 не копировать из модуля в модуль?
А то так себе удобство получается…
=НАЙТИ("В";A2)
Мне нужно в этой формуле вместо конкретного текстового фрагмента (в данном случае"В"
) указать текстовую переменную, например=НАЙТИ(текст.перем;A2)
, поскольку функция используется в цикле и искомые значения разные. Как я могу это сделать?Привет, Віталій!
Если текстовую переменную назвать myTxt, тогда
"=НАЙТИ(" & myTxt & ";A2)"
Здравствуйте, Евгений.
Был немного занят, поэтому не сразу попытался применить формулу по Вашему совету.
Но… В моем примере (вместо текстового значения
"В"
для большей наглядности искал"cloud"
) получается подстановка в формулу=НАЙТИ(cloud;$A2)
, то есть текстовое значение не в кавычках, следовательно, в ячейке А2 ищется не"cloud"
, а нечто по ссылке наcloud
.Фрагмент кода таков:
В результате получаем
=НАЙТИ(cloud;$A2)
. А хочется очень получить=НАЙТИ("cloud";$A2)
Я не сильный специалист в VBA, понимаю, что с основами проблемы, но задача у меня разовая и ради нее выучить язык досконально кажется не очень целесообразным. Поиск в интернете по теме синтаксиса, подстановок в формулы переменных значений делал, но все как-то или бедно, или не о том. Буду благодарен, если поможете заключить cloud в кавычки в итоговой формуле.
Віталій, добавьте кавычки к строке, присваиваемой переменной:
Спасибо огромное, у меня получилось. Уррра.
Но как-то все-таки сложно, не думаю, что это так редко встречающаяся операция — поиск изменяющегося в цикле текста, почему это не предусмотрено стандартными средствами языка? Вопрос не Вам, конечно.
А вот еще вопрос, который мучает меня много дней.
Если, находясь на рабочем листе, выделить диапазон ячеек и нажать Ctrl-F, в открывшейся форме поиска ввести искомую строку и нажать «Найти все» — получим список всех найденных ячеек с вхождением искомого текста. А ниже, типа в строке статистики, написано, например, 227 ячеек найдено. Так вот вопрос: существует ли какой-либо способ получить вот эту цифру 227 из примера средствами VBA.
Я реализовал эту задачу с помощью цикла, но это занимает на порядок(-ки) больше времени…
Пожалуй, получить такую цифру средствами VBA невозможно. Но вы можете ускорить подсчет вхождений искомого текста, присвоив значения ячеек диапазона переменной массива, так как в массивах циклы работают намного быстрее.Присвоил значения ячеек диапазона переменной массива, но тут опять незадача: известные мне функции поиска, такие как ПОИСК, НАЙТИ, ПСТР… легко работают с данными в ячейках таблицы, но я не смог заставить их работать с элементами массива. Или не те инструменты рассматриваю?
Задача заключается в том, чтобы найти количество вхождений заданного текста в ячейках одного столбца, текст этот в ячейке может быть как целиком, так и являться фрагментом ячейки (или элемента массива после присвоения массиву значений ячеек заданного диапазона). С виду элементарная задача, но при большом количестве данных (в моем случае ок. 6000 строк) приходится долго смотреть в потолок.
И еще обнаружил странную вещь: если вот это
Віталій, похоже я нашел, как получить число ячеек с искомым текстом в заданном диапазоне средствами VBA. Попробуйте метод WorksheetFunction.CountIf.
Неработающую функцию
=MUTCH
замените на=MATCH
.Опечатка.
Спасибо, попробую уже позже, устал бороться.
Здравствуйте! сделал по инструкции в статье функцию. при попытке компиляции выдает ошибку
«Syntax error». Что тут не так?
В качестве ошибочного элемента выделяет оператор присваивания (
:=
)Здравствуйте, Даниил!
Попробуйте убрать пробелы перед и после
:=
.Здравствуйте! Попробовал убрать пробелы. Не проходит. мне надо чтобы в нижней части формы пользовательской функции были описание назначения функции и аргументов и не было сообщения «справка недоступна». как понимаю это можно сделать только через метод MacroOptions
Даниил, между аргументами перед знаком переноса на другую строку должна быть запятая с пробелом:
Проверил у себя — работает.
Здравствуйте! А как можно сделать одну функцию для кучи кнопок
код кнопок идентичный
Вопрос можно как-то сделать одну функцию а кнопками её вызывать?
Здравствуйте, Юрий!
Вставьте общую процедуру в стандартный модуль и вызывайте ее из процедур нажатия кнопок. Например, для вызова процедуры
Primer()
из модуляModule1
код будет таким:Ключевое слово Call можно не указывать, но по нему сразу видно, что происходит вызов сторонней процедуры.
Спасибо
Обсуждение закрыто.