В отличии от языка VBScript, VBA процедуры классифицируются не на два типа (процедура-функция и процедура-подпрограмма), а четырех типов: процедура-функция, процедура-подпрограмма, процедура свойств и обработка событий. Также существуют некоторые дополнения в плане передачи параметров (по значению или по ссылке). Третьим моментом является область видимости – в VBA вызов процедуры может осуществляться как в пределах текущего модуля (макроса), так и за его пределами – во всех проектах. Все это обусловлено тем, что VBA – это не столько язык программирования, сколько программный пакет, с возможностью создания форм и проектов.
Давайте сначала кратко рассмотрим типы VBA процедур:
Подпрограммы – блоки кода заключенные в конструкцию Sub …. End Sub. Сама по себе подпрограмма не возвращает никакого значения, а просто выполняет прописанные в ней команды.
Функции – также блок кода, но прописанный в конструкцию Function … End Function. После выполнения функции возвращается определенное значение, доступ к которому можно получить через имя VBA функции.
Помимо этого, стоит упомянуть про обработку событий (нажатие кнопки клавиатуры или перемещение мыши) и доступ к объектам, но это отдельная тема.
VBA процедуры типа Sub – подпрограммы
После того как вы добавили в проект новый модуль, для объявления процедуры VBA нужно ее заключить в специальную конструкцию:
Sub ИмяПодпрограмм([аргументы])
Операторы
[Exit Sub]
операторы
End Sub
После ключевого слова Sub следует имя подпрограммы, в круглых скобках можно указывать или не указывать аргументы. Аргументы – это переменные (параметры), значение которых может обрабатываться, аргументы разделяются запятыми. Конструкция Exit Sub также не является обязательной, она говорит том, что нужно произвести выход из подпрограммы и продолжить выполнение кода, следующего после выражения End Sub.
Вызов VBA процедуры осуществляется с помощью ключевого слова call, например, Call MySub.
Давайте напишем простой пример: добавьте в проект новую форму и новый модуль. На поверхность формы добавьте два текстовых поля (TextBox), одну метку (Label) и одну кнопку (CommandButton). Создайте связь между формой и модулем, прописав в редакторе кода для модуля:
Sub SubModule() SubForm.Show End Sub
Я назвал форму SubForm, а модуль – SubModule, за имя отвечает свойство Name.
Теперь в редакторе кода для формы пропишите:
'************************************ ' Вычисление гипотенузы '************************************ ' процедура VBA принимает два параметра Sub Hipotenuze(a, b) Dim c ' Проверка, если значения равны нулю If TextBox1.Text = 0 Or TextBox2.Text = 0 Then a = 1: b = 1 End If ' вычисление гипотенузы c = Sqr(a ^ 2 + b ^ 2) Label1.Caption = "Гипотенуза: " & c End Sub ' Обработка нажатия на кнопку Private Sub CommandButton1_Click() Dim Ta, Tb Ta = TextBox1.Text Tb = TextBox2.Text ' vba вызов процедуры Hipotenuze Call Hipotenuze(Ta, Tb) End Sub ' Настройка свойств при запуске формы Private Sub UserForm_Initialize() Label1.Caption = "" Label1.FontSize = 15 Label1.ForeColor = vbBlue CommandButton1.Caption = "Найти" TextBox1.Text = 5 TextBox2.Text = 5 End Sub
Тут все предельно просто, вначале мы объявили процедуру Hipotenuze, которой будут передаваться два аргумента, далее происходит проверка на нулевые значения. Вызов происходит при нажатии на кнопку, находящуюся на форме, параметрами будут значения, хранящиеся в текстовых полях TextBox1 и TextBox2. Результат отображается в метке Label1.
Вызов процедуры VBA может осуществляться и без использования ключевого слова Call, в таком случае, параметры не надо заключать в круглые скобки. Так же, при определении аргументов можно явно указать тип данных, например:
Sub MySub (a As Integer, b As String) … End Sub
Дополнительные особенности:
Static – данное ключевое слово, прописанное перед ключевым словом Sub позволяет сохранять в памяти значения всех переменных после выполнения процедуры. Его мы рассматривали в с статье – переменные VBA.
ParamArray – данное ключевое слово позволяет передавать процедуре переменное количество параметров, оно может использоваться только для последнего элемента в списке аргументов.
ParamArray нельзя использовать вместе со словами ByRef, ByVal или Optional, например:
'************************************ ' Передача параметров '************************************ ' процедура VBA принимает два параметра Sub MyArguments(a As Integer, ParamArray b()) Dim elem, s As String Label1.Caption = a For Each elem In b s = s & elem & " " Next Label2.Caption = s End Sub ' Обработка нажатия на кнопку Private Sub CommandButton1_Click() MyArguments 1, 5, 6, 100, "строка" End Sub ' Настройка свойств при запуске формы Private Sub UserForm_Initialize() Label1.Caption = "" Label1.FontSize = 15 Label1.ForeColor = vbBlue Label2.Caption = "" Label2.FontSize = 15 Label2.ForeColor = vbRed CommandButton1.Caption = "Вывести" End Sub
Как видим, мы фактически с помощью ParamArray показываем, что передаем массив, для его обработки мы использовали оператор For …. Each. Тут мы передали при вызове VBA процедуры пять параметров, при этом, первый будет храниться в аргументе a, а остальные в аргументе b, который обрабатывается как массив.
Optional – позволяет указать, что аргумент не является обязательным и одновременно задать значение по умолчанию.
Например:
'************************************ ' Передача параметров '************************************ Sub MyArguments(Optional a As Integer = 5, Optional b As String = " плюс ", Optional c As Integer = 10) Label1.Caption = a & b & c End Sub Private Sub CommandButton1_Click() MyArguments End Sub Private Sub CommandButton2_Click() MyArguments 100, " минус ", 50 End Sub Private Sub CommandButton3_Click() MyArguments 20, , 30 End Sub Private Sub UserForm_Initialize() Label1.Caption = "" Label1.FontSize = 15 Label1.ForeColor = vbBlue CommandButton1.Caption = "Вариант 1" CommandButton2.Caption = "Вариант 2" CommandButton3.Caption = "Вариант 3" End Sub
В данном примере на поверхности формы находится всего одна метка и три кнопки. Каждая из кнопок будет производить VBA вызов процедуры MyArguments с различными значениями.
Передача параметров по ссылке и по значению – по умолчанию, при вызове процедуры все параметры ей передаются по ссылке. Передача по ссылке – в простом варианте, это передача адреса по которому хранится значение. При передаче параметра по ссылке, передается не адрес, а копия значения.
Что бы все стало понятно, рассмотрим следующий пример:
'************************************ ' Передача параметров '************************************ Sub MySub1() Dim MyVar MyVar = 100 Call MySumm1(MyVar) Label1.Caption = "Передача по ссылке " & MyVar End Sub Sub MySub2() Dim MyVar MyVar = 100 Call MySumm2(MyVar) Label2.Caption = "Передача по значению " & MyVar End Sub Sub MySumm1(ByRef a) a = a + 100 End Sub Sub MySumm2(ByVal a) a = a + 100 End Sub Private Sub CommandButton1_Click() MySub1 MySub2 End Sub Private Sub UserForm_Initialize() Label1.Caption = "" Label1.FontSize = 15 Label1.ForeColor = vbBlue Label2.Caption = "" Label2.FontSize = 15 Label2.ForeColor = vbRed CommandButton1.Caption = "Проверить" End Sub
MySub1 – тут происходит объявление переменной MyVar и присвоение ей значения 100, далее в теле происходит вызов VBA процедуры MySumm1, ей в качестве параметры мы передаем значение переменной MyVar – 100. Сама процедура MySumm принимает значение по ссылке, на что указывает ключевое слово ByRef, к принятому значению прибавляется число 100. Стоит обратить внимание, что ByRef можно было и не писать. После VBA вызова процедуры MySumm1 происходит запись значения переменной MyVar в свойство Caption объекта Label1, в итоге, отобразится число 200.
MySub2 – аналог предыдущей процедуры, но тут происходит вызов MySumm2, в которой происходит передача параметров по значению, о чем говорит ключевое слово ByVal, в итоге, значение переменной MyVar не изменится.
VBA процедуры типа Function – функции
Пользовательским функциям языка VBA присущи практически те же правила, что и подпрограммам. Общая структура функции:
Function ИмяФункции ([аргументы]) [As ТипДанных]
Операторы
[Exit Function]
Операторы
[ИмяФункции=Выражение]
End Function
Видим, что тут при объявлении функции можно указать ее тип, данный тип будет содержать возвращаемое значение. Что бы функция возвращала значение, в конце нужно его присвоить переменной с именем функции, например:
Function Summ(a As Integer, b As Integer) As Integer Summ = a + b End Function Private Sub CommandButton1_Click() Label1.Caption = "Сумма 10 и 20: " & Summ(10, 20) End Sub Private Sub UserForm_Initialize() Label1.Caption = "" Label1.FontSize = 15 Label1.ForeColor = vbBlue CommandButton1.Caption = "Сумма" End Sub
Ранее были рассмотрены процедуры VBA. В настоящей заметке рассмотрены функции VBА.[1] Функция — это процедура VBA, которая выполняет вычисления и возвращает значение. Функции можно использовать в коде VBA или в формулах Excel. Процедуру можно рассматривать как команду, которая выполняется пользователем или другой процедурой. С другой стороны, функция обычно возвращает отдельное значение (или массив) подобно функциям рабочих листов Excel и встроенным функциям VBA.
Рис. 1. Применение пользовательской функции в формуле рабочего листа
Скачать заметку в формате Word или pdf, примеры в архиве (политика безопасности провайдера не позволяет загружать файлы Excel с поддержкой макросов)
Excel содержит более 400 встроенных функций. Если этого количества недостаточно, можно создавать пользовательские функции с помощью VBA. Однако следует отметить, что функции VBA, используемые в формулах, обычно выполняются медленнее, чем встроенные функции Excel. Пользовательские функции отображаются в диалоговом окне Мастер функций наряду со встроенными функциями Excel.
Пример пользовательской функции
Начнем с примера – функции RemoveVowels (УдалитьГласные), которая принимает текстовый аргумент, удаляет все гласные буквы и возвращает текст, состоящий только из согласных.
Function RemoveVowels(txt) As String
'
Удаляет все гласные звуки из аргумента txt
Dim i As Long
RemoveVowels = "
"
For i = 1 To Len(txt)
If Not ucase(Mid(txt, i, 1)) Like "
[AEIOUАЕИОУЮЭЯ]"
Then
RemoveVowels = RemoveVowels & Mid(txt, i, 1)
End If
Next i
End Function
Код пользовательских функций, которые используются в формуле рабочего листа, вводите в обычном модуле VBA. Если вы поместите пользовательские функции в модуле Лист, в Пользовательской форме или в модуле ЭтаКнига, они не будут выполняться в формулах.
Функцию RemoveVowels можно использовать, например, в формуле в ячейке В1 (рис. 1) =RemoveVowels (А1). Вы также можете создавать вложенные пользовательские функции и сочетать их в формулах с обычными функциями Excel. Например, =ПРОПИСН(RemoveVowels(А1))
Пользовательские функции можно применять не только в формулах рабочего листа, но и в процедурах VBA. Например, процедура ZapTheVowels() сначала отображает окно для ввода текста пользователем, затем обрабатывает этот текст функцией RemoveVowels, и наконец использует встроенную функцию VBA MsgBox для отображения результатов (рис. 2). Первоначальные данные отображаются в заголовке окна сообщения.
Sub ZapTheVowels()
Dim UserInput As String
UserInput = InputBox("
Введите текст:"
)
MsgBox RemoveVowels(UserInput), vbInformation, UserInput
End Sub
Рис. 2. Применение пользовательской функции в процедуре VBA
Помните, что функции, используемые в формулах рабочего листа, — «пассивные». Они не могут изменять содержимое рабочего листа. Например, нельзя написать функцию, которая будет изменять цвет текста в ячейке в зависимости от значения этой ячейки. Функция возвращает значение, но не может выполнять операции над объектами.
Из этого правила имеется одно исключение. Вы можете изменить текст комментария ячейки с помощью пользовательской функции VBA:
Function ModifyComment(Cell As Range, Cmt As String)
Cell.Comment.Text Cmt
End Function
Например, можно ввести в ячейку В1 формулу =ModifyComment(А1,»Комментарий был изменен»). Функция не работает, если в ячейке А1 отсутствует комментарий.
Рассмотрим код функции RemoveVowels подробнее. Функция начинается с ключевого слова Function, а не Sub, после которого указывается название функции (RemoveVowels). Эта специальная функция использует только один аргумент (Txt), заключенный в скобки. Ключевое слово As String определяет тип данных значения, которое возвращает функция. (Excel по умолчанию использует тип данных Variant, если тип данных не определен.)
Вторая строка — простой комментарий (необязательный), который описывает выполняемые функцией действия. После комментария приведен оператор Dim, который объявляет переменную (i), применяемую в функции. Тип этой переменной — Long. Далее в качестве переменной используется имя функции. Как только функция завершает свое выполнение, возвращается текущее значение переменной, которое соответствует названию функции.
Следующие пять инструкций образуют цикл For-Next. Процедура циклически просматривает каждый символ введенного текста, создавая на их основе строку. Первая инструкция в цикле использует функцию VBA Mid, которая возвращает единственный символ строки ввода, а также преобразует этот символ в символ верхнего регистра. Затем этот символ сравнивается со списком символов с помощью оператора VBA Like (подробнее см. Оператор Like). Другими словами, значение выражения If будет True, если символ отличен от символов А, Е, I, O, U, А, Е, И, О, У, Ы, Э, Ю и Я. В подобных случаях символ добавляется к переменной RemoveVowels.
По завершении цикла из строки ввода удаляются все гласные буквы. Эта строка и является значением, возвращаемым функцией RemoveVowels. Процедура завершается оператором End Function. (Альтернативный код – RemoveVowels2, выполняющий ту же задачу приведен в модуле VBA приложенного Excel-файла.)
Синтаксис функции
Для объявления функции применяется следующий синтаксис (элементы аналогичны обычной процедуре; подробнее см. Работа с процедурами VBA).
[Public | Private][Static] Function имя ([список_аргументов])[As тип]
[инструкции]
[имя = выражение]
[Exit Function]
[инструкции]
[имя = выражение]
End Function
Значение всегда присваивается названию функции минимум один раз и, как правило, тогда, когда функция завершила выполнение. Создание пользовательской функции начните с создания модуля VBA (можно также использовать существующий модуль). Введите ключевое слово Function, после которого укажите название функции и список ее аргументов (если они есть) в скобках. Вы также можете объявить тип данных значения, которое возвращает функция, используя ключевое слово As (это делать необязательно, но рекомендуется). Вставьте код VBA, выполняющий требуемые действия, и убедитесь, что необходимое значение присваивается переменной процедуры, соответствующей названию функции, минимум один раз в теле функции. Функция заканчивается оператором End Function.
Имена функций подчиняются тем же правилам, что и имена переменных. Если вы планируете использовать функцию в формуле рабочего листа, убедитесь, что название не имеет форму адреса ячейки. Также не присваивайте функциям имена, которые соответствуют названиям встроенных функций Excel. Если область действия функции не задана, то по умолчанию подразумевается Public. Функции, объявленные как Private, не отображаются в диалоговом окне Мастер функций.
Функцию можно вызвать одним из следующих способов:
- вызвать ее из другой процедуры;
- включить ее в формулу рабочего листа;
- включить в формулу условного форматирования;
- вызвать ее в окне отладки VBE (Immediate). Этот метод обычно применяется на этапе тестирования (рис. 3).
Рис. 3. Вызов функции в окне отладки
В отличие от процедур, функции не отображаются в диалоговом окне Макрос (меню Разработчик –> Код –> Макросы; или Alt+F8).
Аргументы функций
Аргументы могут представляться переменными (в том числе массивами), константами, символьными данными или выражениями. Некоторые функции не имеют аргументов. Функции имеют как обязательные, так и необязательные аргументы.
Функции без аргументов
В Excel есть несколько встроенных функций, не имеющих аргументов, например, СЛЧИС, СЕГОДНЯ, ТДАТА. Несложно создать аналогичные пользовательские функции. Например:
Function User()
'
Возвращает имя пользователя
User = Application.UserName
End Function
При вводе формулы =User() ячейка возвращает имя текущего пользователя (рис. 4). Обратите внимание: при использовании функции без аргумента в формуле рабочего листа необходимо указать пустые скобки.
Рис. 4. Формула =User() возвращает имя текущего пользователя
Пользовательские функции ведут себя подобно встроенным функциям Excel. Обычно пользовательская функция пересчитывается тогда, когда это нужно, т.е. в случае изменения одного из аргументов функции. Однако вы можете выполнять пересчет функций чаще. Функция пересчитывается при изменении любой ячейки, если в процедуру добавлен оператор
Application.Volatile True
Метод Volatile объекта Application имеет один аргумент (True или False). Если функция выделена как volatile (изменяемая), она пересчитывается всякий раз, когда изменяется любая ячейка листа. При использовании аргумента False метода Volatile функция пересчитывается только тогда, когда в результате пересчета изменяется один из ее аргументов.
В Excel есть встроенная функция СЛЧИС. Но мне не слишком понравилось, что случайные числа изменяются при каждом пересчете рабочего листа. Поэтому я разработал функцию, которая возвращает случайные числа, не изменяющиеся при пересчете формул. Для этого была использована встроенная функция VBA Rnd:
Function StaticRand()
'
Возвращает случайное число, не изменяемое при пересчете формул
StaticRand = Rnd()
End Function
Значения, полученные с помощью этой формулы, никогда не изменяются. Но у пользователя остается возможность принудительного пересчета формулы с помощью комбинации клавиш <Ctrl+Alt+F9>.
Функция с одним аргументом
Допустим вам нужно подсчитать комиссионные, зависящие от объема продаж. Вычисления основываются на следующей таблице значений:
Рис. 5. Таблица комиссионных
Существует несколько способов вычислить комиссионные. Например, с помощью следующей формулы (если объем продаж поместить в ячейку D1):
=ЕСЛИ(И(D1>=0;D1<=9999,99);D1*0,08;ЕСЛИ(И(D1>=10000;D1<=19999,99);D1*0,105; ЕСЛИ(И(D1>=20000;D1<=39999,99);D1*0,12;ЕСЛИ(D1>=40000;D1*0,14))))
Эта формула неудачна по нескольким причинам. Во-первых, она сложна, ее нелегко набрать, и в дальнейшем редактировать. Во-вторых, значения строго определены в формуле, из-за чего ее сложно изменять. Гораздо лучше использовать ВПР (рис. 6).
Рис. 6. Использование функции ВПР для вычисления комиссионных
Еще лучше (тогда не нужно использовать таблицу соответствия) создать пользовательскую функцию:
Function Commission(Sales)
Const Tier1 = 0.08
Const Tier2 = 0.105
Const Tier3 = 0.12
Const Tier4 = 0.14
'
Вычисление комиссионных с продаж
Select Case Sales
Case 0 To 9999.99: Commission = Sales * Tier1
Case 10000 To 19999.99: Commission = Sales * Tier2
Case 20000 To 39999.99: Commission = Sales * Tier3
Case Is >= 40000: Commission = Sales * Tier4
End Select
End Function
После ввода в модуль VBA эту функцию можно использовать в формуле на рабочем листе или вызвать из других процедур VBA. При вводе в ячейку следующей формулы будет получен результат 3000:
=Commission(В2)
Используйте аргументы, а не ссылки на ячейки. Все применяемые в пользовательской функции диапазоны должны передаваться в качестве аргументов. Рассмотрим функцию, которая возвращает значение в ячейке А1, умноженное на 2.
Function DoubleCell()
DoubleCell = Range("
Al"
) * 2
End Function
Хотя эта функция работает, в некоторых случаях она выдает неправильный результат. Причина в том, что вычислительный механизм Excel не учитывает диапазоны, которые не передаются в качестве аргументов. Вследствие этого иногда перед возвратом функцией значения, не вычисляются все связанные величины. Следует также написать функцию DoubleCell, в качестве аргумента которой передается значение ячейки А1.
Function DoubleCell(cell)
DoubleCell = cell * 2
End Function
Функция с двумя аргументами
Представим, что менеджер, о котором речь шла выше, внедряет новую политику, разработанную для уменьшения текучести кадров: общая сумма комиссионных, подлежащих выплате, увеличивается на 1% за каждый год, который служащий проработал в компании. Изменим пользовательскую функцию Commission так, чтобы она принимала два аргумента. Новый аргумент представляет количество лет, отработанных сотрудником в компании. Назовем эту новую функцию Commission2:
Function Commission2(Sales, Years) As Single
'
Вычисление комиссионных с продаж на основе
'
длительности стажа
Commission2 = Commission(Sales) + _
(Commission(Sales) * Years / 100)
End Function
Функция с аргументом в виде массива
В качестве аргументов функции могут принимать один или несколько массивов, обрабатывать этот массив (массивы) и возвращать единственное значение. Функция, представленная ниже, принимает в качестве аргумента массив и возвращает сумму его элементов.
Function SumArray(List) As Double
Dim Item As Variant
SumArray = 0
For Each Item In List
If WorksheetFunction.IsNumber(Item) Then _
SumArray = SumArray + Item
Next Item
End Function
Функция Excel ЕЧИСЛО проверяет, является ли каждый элемент числом, прежде чем добавить его к общему целому. Добавление этого простого оператора проверки данных устраняет ошибки несоответствия типов при попытке выполнить арифметическую операцию над строкой.
Функция с необязательными аргументами
Многие встроенные функции Excel имеют необязательные аргументы. Пример — функция ЛЕВСИМВ, возвращающая символы с левого края строки. Она имеет следующий синтаксис:
ЛЕВСИМВ(текст, кол_символов)
Первый аргумент — обязательный, в отличие от второго. Если не указан второй аргумент, Excel предполагает значение 1.
Пользовательские функции, разработанные в VBA, также могут иметь необязательные аргументы. Необязательный аргумент вы зададите, если введете перед именем аргумента ключевое слово Optional. В списке аргументов необязательные аргументы определяются после всех обязательных. Например:
Function User2(Optional Uppercase As Variant)
If IsMissing(Uppercase) Then Uppercase = False
User2 = Application.UserName
If Uppercase Then User2 = UCase(User2)
End Function
Если аргумент равен False или опущен, то имя пользователя возвращается без каких-либо изменений. Если же аргумент функции True, то имя пользователя возвращается в символах верхнего регистра (с помощью VBA-функции Ucase). Обратите внимание на первый оператор функции — он содержит VBA-функцию IsMissing, которая определяет наличие аргумента. Если аргумент отсутствует, оператор присваивает переменной Uppercase значение False (задано по умолчанию).
Функция VBA, возвращающая массив
VBA содержит весьма полезную функцию с названием Array. Она возвращает значение с типом данных Variant, которое содержит массив (т.е. несколько значений). Если вы не знакомы с формулами массивов в Excel, предлагаю начать с Excel. Введение в формулы массива. Формула массива вводится в ячейку после нажатия <Ctrl+Shift+Entei>. Excel добавляет вокруг формулы скобки, чтобы указать, что это формула массива.
Функция MonthNames — простой пример применения функции Array в пользовательской функции.
Function MonthNames()
MonthNames = Array("
Январь"
, "
Февраль"
, "
Март"
, _
"
Апрель"
, "
Май"
, "
Июнь"
, "
Июль"
, "
Август"
, _
"
Сентябрь"
, "
Октябрь"
, "
Ноябрь"
, "
Декабрь"
End Function
Функция MonthNames возвращает горизонтальный массив названий месяцев. На рабочем листе выделите 12 ячеек, введите формулу =MonthNames() и нажмите <Ctrl+Shift+Enter>. Если необходимо сгенерировать вертикальный массив названий месяцев, выделите вертикальный диапазон, введите формулу =ТРАНСП(MonthNames()) и нажмите <Ctrl+Shift+Enter>.
Функция, возвращающая значение ошибки
В VBA содержатся встроенные константы для обозначения ошибок, которые должна возвращать пользовательская функция (эти значения — ошибки выполнения формул Excel, а не ошибки выполнения кода VBA):
- xlErrDivO (для ошибки #ДЕЛ/0!);
- xlErrNA (для ошибки #Н/Д);
- xlErrName (для ошибки #ИМЯ?);
- xlErrNull (для ошибки #ПУСТО!);
- xlErrNum (для ошибки #ЧИСЛО!);
- xlErrRef (для ошибки #ССЫЛ!);
- xlErrValue (для ошибки #ЗНАЧ!).
Ниже приведена преобразованная функция RemoveVowels (см. пример в начале). Конструкция If-Then применяется для выполнения альтернативного действия в случае, когда аргумент не является текстовым. Эта функция вызывает функцию Excel ЕТЕКСТ, которая определяет, содержит ли аргумент текст. Если ячейка содержит текст, то функция возвращает нормальный результат. Если же ячейка содержит не текст (или пуста), то функция возвращает ошибку #ЗНАЧ!
Function RemoveVowels3(txt) As Variant
'
Удаляет все гласные буквы из аргумента Txt
'
Возвращает ошибку #ЗНАЧ!, если аргумент — не строка
Dim i As Long
RemoveVowels3 = "
"
If Application.WorksheetFunction.IsText(txt) Then
For i = 1 To Len(txt)
If Not UCase(Mid(txt, i, 1)) Like "
[AEIOUАЕИОУЮЭЯ]"
Then
RemoveVowels3 = RemoveVowels3 & Mid(txt, i, 1)
End If
Next i
Else
RemoveVowels3 = CVErr(xlErrValue)
End If
End Function
Обратите внимание, что был изменен тип данных для возвращаемого функцией значения. Поскольку функция может возвращать что-то еще, кроме строки, тип данных был изменен на Variant.
Функция с неопределенным количеством аргументов
Существует возможность создавать пользовательские функции, имеющие неопределенное количество аргументов. Примените в качестве последнего (или единственного) аргумента массив и добавьте перед ним ключевое слово ParamArray (ParamArray относится только к последнему аргументу в списке аргументов процедуры. Он всегда имеет тип данных Variant и всегда является необязательным аргументом). Следующая функция возвращает сумму всех аргументов, в качестве которых может выступать, как одно значение (ячейка), так и диапазон.
Function SimpleSum(ParamArray arglist() As Variant) As Double
Dim cell As Range
Dim arg As Variant
For Each arg In arglist
For Each cell In arg
SimpleSum = SimpleSum + cell
Next cell
Next arg
End Function
Отладка функций
При использовании формулы на рабочем листе для тестирования функции происходящие в процессе выполнения ошибки не отображаются в знакомом диалоговом окне сообщений. Формула просто возвращает значение ошибки (#ЗНАЧ!). К счастью, это не представляет большой проблемы при отладке функций, так как всегда существует несколько обходных путей.
- Поместите в важных местах функцию MsgBox, чтобы контролировать значения отдельных переменных.
- Протестируйте функцию, вызвав ее из процедуры, а не в формуле рабочего листа. Ошибки в процессе выполнения отображаются обычным образом.
- Определите точку остановки в функции и просмотрите функцию пошагово. При этом можно воспользоваться всеми стандартными инструментами отладки. Чтобы добавить точку остановки, поместите курсор в операторе, в котором вы решили приостановить выполнение, и выберите команду Debug –> Toggle Breakpoint (Отладка –> Точка остановки) или нажмите <F9>.
- Используйте в программе один или несколько временных операторов Print (Отладка, Печать), чтобы отобразить значения в окне Immediate редактора VBA. Например, чтобы проконтролировать циклически изменяемое значение, используйте следующий метод:
Рис. 7. Используйте окно отладки для отображения результатов при выполнении функции
В данном случае значения двух переменных, Ch и i, выводятся в окне отладки (Immediate) всякий раз, когда в программе встречается оператор Debug.Print. Встаньте курсором в любое место процедуры Test() и нажмите F5. На рис. 7 показан результат для случая, когда функция принимает аргумент TusconArizona.
Использование метода MacroOptions
Можно воспользоваться методом MacroOptions объекта Application, который позволяет включить в состав встроенных функций Excel разработанные вами функции. Этот метод позволяет:
- добавить описание функции (начиная с версии Excel 2010;
- указать категорию функции;
- добавить описание аргументов функции.
Sub DescribeFunction()
Dim FuncName As String
Dim FuncDesc As String
Dim FuncCat As Long
Dim Arg1Desc As String, Arg2Desc As String
FuncName = "
Draw"
FuncDesc = "
Содержимое случайной ячейки диапазона"
FuncCat = 5 '
Ссылки и массивы
Arg1Desc = "
Диапазон, который содержит значения"
Arg2Desc = "
(не обязательный) Если False или отсутствует, _
функция Rnd не пересчитывается. "
Arg2Desc = Arg2Desc & "
Если True, функция Rnd пересчитывается "
Arg2Desc = Arg2Desc & "
при любом изменении на листе."
Application.MacroOptions _
Macro:=FuncName, _
Description:=FuncDesc, _
Category:=FuncCat, _
ArgumentDescriptions:=Array(Arg1Desc, Arg2Desc)
End Sub
На рис. 8 показаны диалоговые окна Мастер функций и Аргументы функции после выполнения процедуры DescribeFunction().
Рис. 8. Вид диалоговых окон Мастер функций и Аргументы функции для пользовательской функции
Процедуру DescribeFunction()следует вызывать только один раз. После ее вызова информация, связанная с функцией, сохраняется в рабочей книге. Но если вы модифицировали процедуру, повторите ее вызов.
Если вы не укажете категорию функции с помощью метода MacroOptions, пользовательская функция рабочего листа появится в категории Определенные пользователем диалогового окна Мастер функций. В таблице (рис. 9) перечислены номера категорий, которые можно использовать в качестве значений аргумента Category метода MacroOptions. Обратите внимание, что некоторые из этих категорий (от 10 до 13) обычно не отображаются в диалоговом окне Мастер функций. Если же отнести одну из пользовательских функций в подобную категорию, она появится в диалоговом окне.
Рис. 9. Номера категорий функций
Использование надстроек для хранения пользовательских функций
При желании можно сохранить часто используемые пользовательские функции в файле надстройки. Основное преимущество такого подхода заключается в следующем: функции могут быть применены в формулах без спецификатора имени файла. Предположим, у вас есть пользовательская функция ZapSpaces; она хранится в файле Myfuncs.xlsm. Чтобы применить ее в формуле другой рабочей книги (отличной от Myfuncs.xlsm), необходимо ввести следующую формулу: =Myfuncs.xlsm!ZapSpaces(А1:С12).
Если вы создадите надстройку на основе файла Myfuncs.xlsm и эта надстройка будет загружена в текущем сеансе работы Excel, то ссылку на файл можно пропустить, введя следующую формулу: =ZapSpaces(А1:С12). Создание надстроек будет рассмотрено отдельно.
Потенциальная проблема, которая может возникнуть из-за использования надстроек для хранения пользовательских функций, связана с зависимостью рабочей книги от файла надстроек. Если вы передаете рабочую книгу сотруднику, не забудьте также передать копию надстройки, которая содержит требуемые функции.
Использование функций Windows API
VBA может заимствовать методы из других файлов, которые не имеют ничего общего с Excel или VBA, например, файлы DLL (Dynamic Link Library — динамически подключаемая библиотека), которые используются Windows и другими программами. В результате в VBA появляется возможность выполнять операции, которые без заимствованных методов находятся за пределами возможностей языка.
Windows API (Application Programming Interface — интерфейс прикладного программирования) представляет собой набор функций, доступных программистам в среде Windows. При вызове функции Windows из VBA вы обращаетесь к Windows API. Многие ресурсы Windows, используемые программистами Windows, можно получить из файлов DLL, в которых хранятся программы и функции, подсоединяемые в процессе выполнения программы, а не во время компиляции.
Прежде чем использовать функцию Windows API, ее необходимо объявить вверху программного модуля. Если программный модуль — это не стандартный модуль VBA (т.е. модуль для UserForm, Лист или ЭтаКнига), то API-функцию необходимо объявить, как Private.
Объявление API-функции имеет некоторую сложность — функция должна объявляться максимально точно. Оператор объявления указывает VBA следующее:
- какую API-функцию вы используете;
- в какой библиотеке расположена API-функция;
- аргументы API-функции.
После объявления API-функцию можно использовать в программе VBA.
Рассмотрим пример API-функции, которая отображает имя папки Windows (с помощью стандартных операторов VBA эту задачу порой выполнить невозможно). Для начала объявим API-функцию:
Declare PtrSafe Function GetWindowsDirectoryA Lib "
kernel32"
_
(ByVal lpBuffer As String, ByVal nSize As Long) As Long
Эта функция, имеющая два аргумента, возвращает название папки, в которой установлена операционная система Windows. После вызова этой функции путь к папке Windows будет храниться в переменной lpBuffer, а длина строки пути — в переменной nSize.
Следующий пример отображает результат в окне сообщения:
Sub ShowWindowsDir()
Dim WinPath As String * 255
Dim WinDir As String
WinPath = Space(255)
WinDir = Left(WinPath, GetWindowsDirectoryA _
(WinPath, Len(WinPath)))
MsgBox WinDir, vbInformation, "
Windows Directory"
End Sub
В процессе выполнения процедуры ShowWindowsDir отображается окно сообщения с указанием расположения папки Windows.
Иногда требуется создать оболочку (wrapper) для API-функций. Другими словами, вы создадите собственную функцию, использующую API-функцию. Такой подход существенно упрощает использование API-функции. Ниже приведен пример такой функции VBA:
Function WindowsDir() As String
'
Название папки Windows
Dim WinPath As String * 255
WinPath = Space(255)
WindowsDir = Left(WinPath, GetWindowsDirectoryA _
(WinPath, Len(WinPath)))
End Function
После объявления этой функции можно вызвать ее из другой процедуры: MsgBox WindowsDir(). Можно также использовать эту функцию в формуле рабочего листа: =WindowsDir().
Внимание! Не удивляйтесь сбоям в системе при использовании в VBA функций Windows API. Заранее сохраните свою работу перед тестированием.
Определение состояния клавиши <Shift>
Предположим, вы написали макрос VBA, который будет выполняться с помощью кнопки на панели инструментов. Необходимо, чтобы этот макрос выполнялся по-другому, если пользователь после щелчка на кнопке удерживает клавишу <Shift>. Чтобы узнать о нажатии клавиши <Shift>, можно использовать API-функцию GetKeyState. Функция GetKeyState сообщает о том, нажата ли конкретная клавиша. Функция имеет один аргумент, nVirtKey, который представляет код интересующей вас клавиши.
Ниже приведена программа, которая выявляет, что при выполнении процедуры обработки события Button_Click была нажата клавиша <Shift>. Обратите внимание, что для определения состояния клавиши <Shift> используется константа (принимающая шестнадцатеричное значение), которая затем применяется как аргумент функции GetKeyState. Если GetKeyState возвращает значение меньше 0, это означает, что клавиша <Shift> нажата; в противном случае клавиша <Shift> не нажата. Аналогичную проверку можно устроить для клавиш Ctrl и Alt (рис. 10).
Рис. 10. Проверка нажатия клавиш Shift, Ctrl и Alt
Код функции VBA можно найти в приложенном Excel-файле
Работа с функциями Windows API может быть довольно сложной. Во многих книгах по программированию перечислены операторы объявления API-функций с соответствующими примерами. Как правило, можно просто скопировать выражения объявления и использовать функции, не вникая в их суть. Большинство VBA-программистов в Excel рассматривают API-функции как панацею для решения большинства задач. В Интернете вы найдете сотни вполне надежных примеров, которые можно скопировать и вставить в собственную программу.
В текстовом файле содержатся объявления и константы Windows API. Можно открыть этот файл в текстовом редакторе и скопировать соответствующие объявления в модуль VBA.
[1] По материалам книги Джон Уокенбах. Excel 2010. Профессиональное программирование на VBA. – М: Диалектика, 2013. – С. 287–323.
In this Article
- Creating a Function without Arguments
- Calling a Function from a Sub Procedure
- Creating Functions
- Single Argument
- Multiple Arguments
- Optional Arguments
- Default Argument Value
- ByVal and ByRef
- Exit Function
- Using a Function from within an Excel Sheet
This tutorial will teach you to create and use functions with and without parameters in VBA
VBA contains a large amount of built-in functions for you to use, but you are also able to write your own. When you write code in VBA, you can write it in a Sub Procedure, or a Function Procedure. A Function Procedure is able to return a value to your code. This is extremely useful if you want VBA to perform a task to return a result. VBA functions can also be called from inside Excel, just like Excel’s built-in Excel functions.
Creating a Function without Arguments
To create a function you need to define the function by giving the function a name. The function can then be defined as a data type indicating the type of data you want the function to return.
You may want to create a function that returns a static value each time it is called – a bit like a constant.
Function GetValue() As Integer
GetValue = 50
End Function
If you were to run the function, the function would always return the value of 50.
You can also create functions that refer to objects in VBA but you need to use the Set Keyword to return the value from the function.
Function GetRange() as Range
Set GetRange = Range("A1:G4")
End Function
If you were to use the above function in your VBA code, the function would always return the range of cells A1 to G4 in whichever sheet you are working in.
Calling a Function from a Sub Procedure
Once you create a function, you can call it from anywhere else in your code by using a Sub Procedure to call the function.
The value of 50 would always be returned.
You can also call the GetRange function from a Sub Procedure.
In the above example, the GetRange Function is called by the Sub Procedure to bold the cells in the range object.
Creating Functions
Single Argument
You can also assign a parameter or parameters to your function. These parameters can be referred to as Arguments.
Function ConvertKilosToPounds (dblKilo as Double) as Double
ConvertKiloToPounds = dblKilo*2.2
End Function
We can then call the above function from a Sub Procedure in order to work out how many pounds a specific amount of kilos are.
A function can be a called from multiple procedures within your VBA code if required. This is very useful in that it stops you from having to write the same code over and over again. It also enables you to divide long procedures into small manageable functions.
In the above example, we have 2 procedures – each of them are using the Function to calculate the pound value of the kilos passed to them in the dblKilo Argument of the function.
Multiple Arguments
You can create a Function with multiple arguments and pass the values to the Function by way of a Sub Procedure.
Function CalculateDayDiff(Date1 as Date, Date2 as Date) as Double
CalculateDayDiff = Date2-Date1
End Function
We can then call the function to calculate the amount of days between 2 dates.
Optional Arguments
You can also pass Optional arguments to a Function. In other words, sometimes you may need the argument, and sometimes you may not – depending on what code you are using the Function with .
Function CalculateDayDiff(Date1 as Date, Optional Date2 as Date) as Double
'check for second date and if not there, make Date2 equal to today's date.
If Date2=0 then Date2 = Date
'calculate difference
CalculateDayDiff = Date2-Date1
End Function
VBA Coding Made Easy
Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!
Learn More
Default Argument Value
You can also set the default value of the Optional arguments when you are creating the function so that if the user omits the argument, the value that you have put as default will be used instead.
Function CalculateDayDiff(Date1 as Date, Optional Date2 as Date="06/02/2020") as Double
'calculate difference
CalculateDayDiff = Date2-Date1
End Function
ByVal and ByRef
When you pass values to a function, you can use the ByVal or ByRef keywords. If you omit either of these, the ByRef is used as the default.
ByVal means that you are passing a copy of the variable to the function, whereas ByRef means you are referring to the original value of the variable. When you pass a copy of the variable (ByVal), the original value of the variable is NOT changed, but when you reference the variable, the original value of the variable is changed by the function.
Function GetValue(ByRef intA As Integer) As Integer
intA = intA * 4
GetValue = intA
End Function
In the function above, the ByRef could be omitted and the function would work the same way.
Function GetValue(intA As Integer) As Integer
intA = intA * 4
GetValue = intA
End Function
To call this function, we can run a sub-procedure.
Sub TestValues()
Dim intVal As Integer
'populate the variable with the value 10
intVal = 10
'run the GetValue function, and show the value in the immediate window
Debug.Print GetValue(intVal)
'show the value of the intVal variable in the immediate window
Debug.Print intVal
End Sub
Note that the debug windows show the value 40 both times. When you pass the variable IntVal to the function – the value of 10 is passed to the function, and multiplied by 4. Using the ByRef keyword (or omitting it altogether), will AMEND the value of the IntVal variable. This is shown when you show first the result of the function in the immediate window (40), and then the value of the IntVal variable in the debug window (also 40).
If we do NOT want to change the value of the original variable, we have to use ByVal in the function.
Function GetValue(ByVal intA As Integer) As Integer
intA = intA * 4
GetValue = intA
End Function
Now if we call the function from a sub-procedure, the value of the variable IntVal will remain at 10.
Exit Function
If you create a function that tests for a certain condition, and once the condition is found to be true, you want return the value from the function, you may need to add an Exit Function statement in your Function in order to exit the function before you have run through all the code in that function.
Function FindNumber(strSearch As String) As Integer
Dim i As Integer
'loop through each letter in the string
For i = 1 To Len(strSearch)
'if the letter is numeric, return the value to the function
If IsNumeric(Mid(strSearch, i, 1)) Then
FindNumber= Mid(strSearch, i, 1)
'then exit the function
Exit Function
End If
Next
FindNumber= 0
End Function
The function above will loop through the string that is provided until it finds a number, and then return that number from the string. It will only find the first number in the string as it will then Exit the function.
The function above can be called by a Sub routine such as the one below.
Sub CheckForNumber()
Dim NumIs as Integer
'pass a text string to the find number function
NumIs = FindNumber("Upper Floor, 8 Oak Lane, Texas")
'show the result in the immediate window
Debug.Print NumIs
End Sub
VBA Programming | Code Generator does work for you!
Using a Function from within an Excel Sheet
In addition to calling a function from your VBA code using a sub procedure, you can also call the function from within your Excel sheet. The functions that you have created should by default appear in your function list in the User Defined section of the function list.
Click on the fx to show the Insert Function dialog box.
Select User Defined from the Category List
Select the function you require from the available User Defined Functions (UDF’s).
Alternatively, when you start writing your function in Excel, the function should appear in the drop down list of functions.
If you do not want the function to be available inside an Excel sheet, you need to put the Private word in front of the word Function when you create the function in your VBA code.
Private Function CalculateDayDiff(Date1 as Date, Date2 as Date) as Double
CalculateDayDiff = Date2-Date1
End Function
It will now not appear in the drop down list showing the Excel functions available.
Interestingly enough, however, you can still use the function – it just will not appear in the list when looking for it!
If you have declared the second argument as Optional, you can omit it within the Excel sheet as well as within the VBA code.
You can also use the a function that you have created without arguments in your Excel sheet.
VBA поддерживает два типа подпрограмм: процедуры и функции.
- Функция — это подпрограмма, которая возвращает результат. Вызов функции является выражением, и может использоваться в других выражениях или в правой части оператора присваивания.
- Процедура — это любая подпрограмма, которая не является функцией. Любой макрос VBA является подпрограммой типа «процедура».
Для объявления процедуры в VBA используется ключевое слово Sub:
Sub <имяПроцедуры> [(<списокПараметров>)] <операторы> End Sub
- где:
- <имяПроцедуры> – любой допустимый идентификатор VBA;
- <списокПараметров> – список формальных параметров процедуры, если он пуст, то такая процедура является макросом;
- <операторы> — любая последовательность операторов VBA.
Листинг №.13 Пример объявления процедуры
' Процедура выводит в отладчик максимальное из трех чисел Sub sMax3(A As Long, B As Long, C As Long) If (A > B) And (A > C) Then Debug.Print "Max is "; A ElseIf (B > A) And (B > C) Then Debug.Print "Max is "; B Else Debug.Print "Max is "; C End If End Sub
Синтаксис объявления функции несколько сложнее, чем синтаксис процедуры:
Function <имяФункции> [(<списокПараметров>)] [As <типФункции>] <операторы> . . . <имяФункции> = <возвращаемое_значение> [<операторы>] End Function
- где:
- <имяФункции> — любой допустимый идентификатор;
- <списокПараметров> – список формальных параметров процедуры;
- <типФункции> — имя любого поддерживаемого VBA типа данных;
- <операторы> — любая последовательность операторов VBA.
- <возвращаемое_значение> — результат, передаваемый в вызывающую программу.
Листинг №14. Пример объявления функции
' Функция возвращает максимальное из трех чисел Function fMax3(A As Long, B As Long, C As Long) As Long If (A > B) And (A > C) Then fMax3 = A ElseIf (B > A) And (B > C) Then fMax3 = B Else fMax3 = C End If End Function
Передача параметров в подпрограммы
Подпрограммы VBA могут принимать для обработки формальные параметры, указываемые при объявлении. При вызове они заменяются фактическими параметрами, т.е. реально используемыми в вызывающей программе. В VBA список формальных параметров подпрограммы представляет имена переменных, разделенных запятой. При этом желательно указать тип каждой переменной:
Function | Sub <имяПроцедуры> (<параметр1> As <тип>, _ <параметр2> As <тип>, ..., <параметрN> As <тип>)
Если тип данных параметра не указан, то автоматически будет использован тип Variant. Список параметров может быть пустым как для процедуры, так и для функции. В этом случае после имени процедуры ставятся пустые круглые скобки.
При передаче фактических параметров в подпрограмму может использоваться один из двух различных способов:
- передача по значению;
- передача по ссылке.
При передаче по значению, в подпрограмме создается копия полученного фактического параметра. Все действия внутри подпрограммы выполняются над этой копией и при выходе из подпрограммы все изменения теряются. Если переменная
передается по ссылке, то процедуре или функции будет передан адрес этой переменной. Тем самым вызываемая процедура может изменить значение фактического параметра: если будет измен формальный параметр процедуры, то переданный при вызове ей фактический параметр тоже изменит свое значение.
Способ передачи указывается при описании параметров в строке объявления подпрограммы. Имени параметра может предшествовать один из явных описателей способа передачи:
- ByRef – задает передачу по ссылке;
- ByVal – задает передачу по значению.
По умолчанию выполняется передача по ссылке.
Листниг 15. Передача параметров в подпрограмму
' sample10 - вызывающая программа (макрос) ' ByValByRefDemo - вызываемая процедура Sub sample10() Dim a As Long, b As Long, c As Long ' фактические параметры a = 10 b = 10 c = 10 ByValByRefDemo a, b, c ' передача фактических параметров Debug.Print "2: " & "a = " & a & "; b = " & b & "; c = " & c End Sub Sub ByValByRefDemo(x As Long, ByVal y As Long, ByRef z As Long) ' выполнение действий над формальными параметрами x = x * 2 y = y * 3 z = z * 4 Debug.Print "1: " & "x = " & x & "; y = " & y & "; z = " & z End Sub
Здесь объявлены две процедуры: sample10 и ByValByRefDemo. Процедура sample10 вызывает процедуру ByValByRefDemo и передает ей предварительно инициализированные переменные a, b и c. Процедура ByValByRefDemo получает значения переменных a, b и c в виде формальных параметров x, y и z соответственно, выполняет над ними указанные действия, выводит результат и завершается. После возврата из подпрограммы процедура sample10 выводит значения переменных a, b, c в окно отладчика (рис. 4).
Рис. 4. Передача параметров в подпрограмму
Именованные параметры
При вызове подпрограмм в VBA параметры необходимо передавать в определенном порядке. Отсутствующие необязательные параметры отмечаются запятыми. Нарушение этого правила часто приводит к ошибкам – легко пропустить или поменять местами параметры. Чтобы избежать этих проблем, в VBA можно использовать
именованные параметры функций. Для этого в вызове подпрограммы явно указываются имена параметров (как это было задано при объявлении подпрограммы), каждому из которых присваивается требуемое значение с помощью оператора «:=» («двоеточие_равно»). При использовании именованных параметров можно не обозначать отсутствующие параметры и, кроме того, порядок перечисления параметров может быть произвольным.
Следующий пример показывает два обращения к функции MsgBox, которые имеют один и тот же результат:
... ' обычный вызов MsgBox "Здравствуй, мир!", , "Окно приветствия" ... ' вызов, с использованием именованных параметров MsgBox Prompt:= "Здравствуй, мир!", Title:= "Окно приветствия"
Вызов подпрограмм
Подпрограммы из программ на VBA могут быть вызваны различными способами:
- Процедуру (Sub)с непустым списком параметров можно вызвать только из другой процедуры или функции так:
<имяПроцедуры> <списокПараметров>
Или так:
Call <имяПроцедуры> (<списокПараметров>) ’использована инструкция Call.
-
Процедура с пустым списком параметров рассматривается VBA как командный макрос. Ее также можно вызвать двумя способами:
- из другой процедуры или функции;
- с помощью комбинации клавиш быстрого вызова, команд меню или кнопок панелей инструментов.
-
Функцию
(Function) можно вызывать точно так же, как и процедуру, в виде отдельного оператора. В этом случае возвращаемое функцией значение игнорируется.
Листинг №16. Вызов процедуры
Sub sample11() ' вызывающий макрос Dim usr As String usr = InputBox("Login") Hello usr ' вызов процедуры Helloбез Call Call Hello(usr) ' использование инструкции Call End Sub ' процедура принимает один параметр, формирует строку сообщения, ' выводит сообщение в окно отладчика Sub Hello(usrname As String) Debug.Print "Hello, " & usrname & "!" End Sub
Если в проекте используется несколько подпрограмм с одинаковыми названиями (это возможно, если они в разных модулях), то при их вызове перед именем подпрограммы надо указывать (через точку) имя модуля, в котором процедура расположена:
<имяМодуля>.<имяПроцедуры> <списокФактическихПараметров>
Например
MyModule.MySub fArg, sArg ' вызов процедуры из модуля MyModule
Для вызова общих (Public) подпрограмм из другого проекта дополнительно к именам модуля и подпрограммы указывается имя проекта:
<имяПроекта>.<имяМодуля>.<имяПроцедуры> <списокФактическихПараметров>
Например
someVal = MyProject.MyModule.MyFunc(fArg, sArg) ' вызов функции из проекта MyProject
Прерывание подпрограммы
В случае необходимости, выполнение процедуры или функции может быть прервано досрочно. Для этого нужно использовать инструкцию прерывания Exit Sub. В этом случае синтаксис объявления примет следующий вид (на примере объявления процедуры):
Sub <имяПроцедуры> [(<списокпараметров>)] <операторы> Exit Sub <операторы> End Sub
CC-BY-CA Анатольев А.Г., 29.09.2012
Содержание
- Встроенные функции VBA
- Пользовательские процедуры «Function» и «Sub» в VBA
- Аргументы
- Необязательные аргументы
- Передача аргументов по значению и по ссылке
- VBA процедура «Function»
- Пример VBA процедуры «Function»: Выполняем математическую операцию с 3 числами
- Вызов VBA процедуры «Function»
- Вызов VBA процедуры «Function» из другой процедуры
- Вызов VBA процедуры «Function» из рабочего листа
- VBA процедура «Sub»
- VBA процедура «Sub»: Пример 1. Выравнивание по центру и изменение размера шрифта в выделенном диапазоне ячеек
- VBA процедура «Sub»: Пример 2. Выравнивание по центру и применение полужирного начертания к шрифту в выделенном диапазоне ячеек
- Вызов процедуры «Sub» в Excel VBA
- Вызов VBA процедуры «Sub» из другой процедуры
- Вызов VBA процедуры «Sub» из рабочего листа
- Область действия процедуры VBA
- Ранний выход из VBA процедур «Function» и «Sub»
Встроенные функции VBA
Перед тем, как приступить к созданию собственных функций VBA, полезно знать, что Excel VBA располагает обширной коллекцией готовых встроенных функций, которые можно использовать при написании кода.
Список этих функций можно посмотреть в редакторе VBA:
- Откройте рабочую книгу Excel и запустите редактор VBA (нажмите для этого Alt+F11), и затем нажмите F2.
- В выпадающем списке в верхней левой части экрана выберите библиотеку VBA.
- Появится список встроенных классов и функций VBA. Кликните мышью по имени функции, чтобы внизу окна отобразилось её краткое описание. Нажатие F1 откроет страницу онлайн-справки по этой функции.
Кроме того, полный список встроенных функций VBA с примерами можно найти на сайте Visual Basic Developer Centre.
Пользовательские процедуры «Function» и «Sub» в VBA
В Excel Visual Basic набор команд, выполняющий определённую задачу, помещается в процедуру Function (Функция) или Sub (Подпрограмма). Главное отличие между процедурами Function и Sub состоит в том, что процедура Function возвращает результат, процедура Sub – нет.
Поэтому, если требуется выполнить действия и получить какой-то результат (например, просуммировать несколько чисел), то обычно используется процедура Function, а для того, чтобы просто выполнить какие-то действия (например, изменить форматирование группы ячеек), нужно выбрать процедуру Sub.
Аргументы
При помощи аргументов процедурам VBA могут быть переданы различные данные. Список аргументов указывается при объявлении процедуры. К примеру, процедура Sub в VBA добавляет заданное целое число (Integer) в каждую ячейку в выделенном диапазоне. Передать процедуре это число можно при помощи аргумента, вот так:
Sub AddToCells(i As Integer) ... End Sub
Имейте в виду, что наличие аргументов для процедур Function и Sub в VBA не является обязательным. Для некоторых процедур аргументы не нужны.
Необязательные аргументы
Процедуры VBA могут иметь необязательные аргументы. Это такие аргументы, которые пользователь может указать, если захочет, а если они пропущены, то процедура использует для них заданные по умолчанию значения.
Возвращаясь к предыдущему примеру, чтобы сделать целочисленный аргумент функции необязательным, его нужно объявить вот так:
Sub AddToCells(Optional i As Integer = 0)
В таком случае целочисленный аргумент i по умолчанию будет равен 0.
Необязательных аргументов в процедуре может быть несколько, все они перечисляются в конце списка аргументов.
Передача аргументов по значению и по ссылке
Аргументы в VBA могут быть переданы процедуре двумя способами:
- ByVal – передача аргумента по значению. Это значит, что процедуре передаётся только значение (то есть, копия аргумента), и, следовательно, любые изменения, сделанные с аргументом внутри процедуры, будут потеряны при выходе из неё.
- ByRef – передача аргумента по ссылке. То есть процедуре передаётся фактический адрес размещения аргумента в памяти. Любые изменения, сделанные с аргументом внутри процедуры, будут сохранены при выходе из процедуры.
При помощи ключевых слов ByVal или ByRef в объявлении процедуры можно задать, каким именно способом аргумент передаётся процедуре. Ниже это показано на примерах:
Sub AddToCells(ByVal i As Integer) ... End Sub |
В этом случае целочисленный аргумент i передан по значению. После выхода из процедуры Sub все сделанные с i изменения будут утрачены. |
Sub AddToCells(ByRef i As Integer) ... End Sub |
В этом случае целочисленный аргумент i передан по ссылке. После выхода из процедуры Sub все сделанные с i изменения будут сохранены в переменной, которая была передана процедуре Sub. |
Помните, что аргументы в VBA по умолчанию передаются по ссылке. Иначе говоря, если не использованы ключевые слова ByVal или ByRef, то аргумент будет передан по ссылке.
Перед тем как продолжить изучение процедур Function и Sub более подробно, будет полезным ещё раз взглянуть на особенности и отличия этих двух типов процедур. Далее приведены краткие обсуждения процедур VBA Function и Sub и показаны простые примеры.
VBA процедура «Function»
Редактор VBA распознаёт процедуру Function, когда встречает группу команд, заключённую между вот такими открывающим и закрывающим операторами:
Function ... End Function
Как упоминалось ранее, процедура Function в VBA (в отличие от Sub), возвращает значение. Для возвращаемых значений действуют следующие правила:
- Тип данных возвращаемого значения должен быть объявлен в заголовке процедуры Function.
- Переменная, которая содержит возвращаемое значение, должна быть названа так же, как и процедура Function. Эту переменную не нужно объявлять отдельно, так как она всегда существует как неотъемлемая часть процедуры Function.
Это отлично проиллюстрировано в следующем примере.
Пример VBA процедуры «Function»: Выполняем математическую операцию с 3 числами
Ниже приведён пример кода VBA процедуры Function, которая получает три аргумента типа Double (числа с плавающей точкой двойной точности). В результате процедура возвращает ещё одно число типа Double, равное сумме первых двух аргументов минус третий аргумент:
Function SumMinus(dNum1 As Double, dNum2 As Double, dNum3 As Double) As Double SumMinus = dNum1 + dNum2 - dNum3 End Function
Эта очень простая VBA процедура Function иллюстрирует, как данные передаются процедуре через аргументы. Можно увидеть, что тип данных, возвращаемых процедурой, определён как Double (об этом говорят слова As Double после списка аргументов). Также данный пример показывает, как результат процедуры Function сохраняется в переменной с именем, совпадающим с именем процедуры.
Вызов VBA процедуры «Function»
Если рассмотренная выше простая процедура Function вставлена в модуль в редакторе Visual Basic, то она может быть вызвана из других процедур VBA или использована на рабочем листе в книге Excel.
Вызов VBA процедуры «Function» из другой процедуры
Процедуру Function можно вызвать из другой VBA процедуры при помощи простого присваивания этой процедуры переменной. В следующем примере показано обращение к процедуре SumMinus, которая была определена выше.
Sub main() Dim total as Double total = SumMinus(5, 4, 3) End Sub
Вызов VBA процедуры «Function» из рабочего листа
VBA процедуру Function можно вызвать из рабочего листа Excel таким же образом, как любую другую встроенную функцию Excel. Следовательно, созданную в предыдущем примере процедуру Function – SumMinus можно вызвать, введя в ячейку рабочего листа вот такое выражение:
=SumMinus(10, 5, 2)
VBA процедура «Sub»
Редактор VBA понимает, что перед ним процедура Sub, когда встречает группу команд, заключённую между вот такими открывающим и закрывающим операторами:
VBA процедура «Sub»: Пример 1. Выравнивание по центру и изменение размера шрифта в выделенном диапазоне ячеек
Рассмотрим пример простой VBA процедуры Sub, задача которой – изменить форматирование выделенного диапазона ячеек. В ячейках устанавливается выравнивание по центру (и по вертикали, и по горизонтали) и размер шрифта изменяется на заданный пользователем:
Sub Format_Centered_And_Sized(Optional iFontSize As Integer = 10) Selection.HorizontalAlignment = xlCenter Selection.VerticalAlignment = xlCenter Selection.Font.Size = iFontSize End Sub
Данная процедура Sub выполняет действия, но не возвращает результат.
В этом примере также использован необязательный (Optional) аргумент iFontSize. Если аргумент iFontSize не передан процедуре Sub, то его значение по умолчанию принимается равным 10. Однако же, если аргумент iFontSize передается процедуре Sub, то в выделенном диапазоне ячеек будет установлен размер шрифта, заданный пользователем.
VBA процедура «Sub»: Пример 2. Выравнивание по центру и применение полужирного начертания к шрифту в выделенном диапазоне ячеек
Следующая процедура похожа на только что рассмотренную, но на этот раз, вместо изменения размера, применяется полужирное начертание шрифта в выделенном диапазоне ячеек. Это пример процедуры Sub, которой не передаются никакие аргументы:
Sub Format_Centered_And_Bold() Selection.HorizontalAlignment = xlCenter Selection.VerticalAlignment = xlCenter Selection.Font.Bold = True End Sub
Вызов процедуры «Sub» в Excel VBA
Вызов VBA процедуры «Sub» из другой процедуры
Чтобы вызвать VBA процедуру Sub из другой VBA процедуры, нужно записать ключевое слово Call, имя процедуры Sub и далее в скобках аргументы процедуры. Это показано в примере ниже:
Sub main() Call Format_Centered_And_Sized(20) End Sub
Если процедура Format_Centered_And_Sized имеет более одного аргумента, то они должны быть разделены запятыми. Вот так:
Sub main() Call Format_Centered_And_Sized(arg1, arg2, ...) End Sub
Вызов VBA процедуры «Sub» из рабочего листа
Процедура Sub не может быть введена непосредственно в ячейку листа Excel, как это может быть сделано с процедурой Function, потому что процедура Sub не возвращает значение. Однако, процедуры Sub, не имеющие аргументов и объявленные как Public (как будет показано далее), будут доступны для пользователей рабочего листа. Таким образом, если рассмотренные выше простые процедуры Sub вставлены в модуль в редакторе Visual Basic, то процедура Format_Centered_And_Bold будет доступна для использования на рабочем листе книги Excel, а процедура Format_Centered_And_Sized – не будет доступна, так как она имеет аргументы.
Вот простой способ запустить (или выполнить) процедуру Sub, доступную из рабочего листа:
- Нажмите Alt+F8 (нажмите клавишу Alt и, удерживая её нажатой, нажмите клавишу F8).
- В появившемся списке макросов выберите тот, который хотите запустить.
- Нажмите Выполнить (Run)
Чтобы выполнять процедуру Sub быстро и легко, можно назначить для неё комбинацию клавиш. Для этого:
- Нажмите Alt+F8.
- В появившемся списке макросов выберите тот, которому хотите назначить сочетание клавиш.
- Нажмите Параметры (Options) и в появившемся диалоговом окне введите сочетание клавиш.
- Нажмите ОК и закройте диалоговое окно Макрос (Macro).
Внимание: Назначая сочетание клавиш для макроса, убедитесь, что оно не используется, как стандартное в Excel (например, Ctrl+C). Если выбрать уже существующее сочетание клавиш, то оно будет переназначено макросу, и в результате пользователь может запустить выполнение макроса случайно.
Область действия процедуры VBA
В части 2 данного самоучителя обсуждалась тема области действия переменных и констант и роль ключевых слов Public и Private. Эти ключевые слова так же можно использовать применительно к VBA процедурам:
Public Sub AddToCells(i As Integer) ... End Sub |
Если перед объявлением процедуры стоит ключевое слово Public, то данная процедура будет доступна для всех модулей в данном проекте VBA. |
Private Sub AddToCells(i As Integer) ... End Sub |
Если перед объявлением процедуры стоит ключевое слово Private, то данная процедура будет доступна только для текущего модуля. Её нельзя будет вызвать, находясь в любом другом модуле или из рабочей книги Excel. |
Помните о том, что если перед объявлением VBA процедуры Function или Sub ключевое слово не вставлено, то по умолчанию для процедуры устанавливается свойство Public (то есть она будет доступна везде в данном проекте VBA). В этом состоит отличие от объявления переменных, которые по умолчанию бывают Private.
Ранний выход из VBA процедур «Function» и «Sub»
Если нужно завершить выполнение VBA процедуры Function или Sub, не дожидаясь её естественного финала, то для этого существуют операторы Exit Function и Exit Sub. Применение этих операторов показано ниже на примере простой процедуры Function, в которой ожидается получение положительного аргумента для выполнения дальнейших операций. Если процедуре передано не положительное значение, то дальнейшие операции не могут быть выполнены, поэтому пользователю должно быть показано сообщение об ошибке и процедура должна быть тут же завершена:
Function VAT_Amount(sVAT_Rate As Single) As Single VAT_Amount = 0 If sVAT_Rate <= 0 Then MsgBox "Expected a Positive value of sVAT_Rate but Received " & sVAT_Rate Exit Function End If ... End Function
Обратите внимание, что перед тем, как завершить выполнение процедуры Function – VAT_Amount, в код вставлена встроенная VBA функция MsgBox, которая показывает пользователю всплывающее окно с предупреждением.
Оцените качество статьи. Нам важно ваше мнение:
Вызовы процедур и функций
Вызовы процедур Sub
Вызов обычной процедуры Sub из другой процедуры можно оформить по-разному. Первый способ:
имя список-фактических-параметров
где имя — имя вызываемой процедуры, а список-фактических-параметров — список аргументов, передаваемых процедуре; он должен соответствовать списку аргументов, заданному в описании процедуры. Задать этот список можно разными способами. В простейшем случае в нем перечисляются через запятую значения передаваемых процедуре аргументов в том же порядке, что и в списке аргументов из заголовка процедуры.
Может оказаться, что в одном проекте несколько модулей содержат процедуры с одинаковыми именами. Для различения этих процедур нужно при их вызове указывать имя процедуры через точку после имени модуля, в котором она определена. Например, если каждый из двух модулей Mod1 и Mod2 содержит определение процедуры ReadData, а в процедуре MyProc нужно воспользоваться процедурой из Mod2, этот вызов имеет вид:
Sub Myproc() ... Mod2.ReadData ... End Sub
Если требуется использовать процедуры с одинаковыми именами из разных проектов, добавьте к именам модуля и процедуры имя проекта. Например, если модуль Mod2 входит в проект MyBook, тот же вызов можно уточнить так:
Второй способ вызова процедур связан с использованием оператора Call. В этом случае вызов процедуры выглядит так:
Call имя(список-фактических-параметров)
Обратите внимание на то, что в этом случае список-фактических-параметров заключен в круглые скобки, а в первом случае — нет. Попытка вызывать процедуру без оператора Call, но с заданием круглых скобок является источником синтаксических ошибок особенно для разработчиков с большим опытом программирования на Паскале или С, где списки параметров всегда заключаются в скобки. Следует обратить внимание на одну важную и, пожалуй, неприятную особенность вызова процедур VBA. Если процедура VBA имеет только один параметр, то она может быть вызвана без оператора Call и с использованием круглых скобок, не сообщая об ошибке вызова. Это было бы не так страшно, если бы возвращался правильный результат. К сожалению, это не так, проиллюстрируем сказанное примером:
Public Sub MyInc(ByRef X As Integer) X = X + 1 End Sub Public Sub TestInc() Dim X As Integer X = 1 'Вызов процедуры с параметром, заключенным в скобки, 'синтаксически допустим, но работает не корректно! MyInc (X) Debug.Print X 'Корректный вызов MyInc X Debug.Print X 'Это тоже корректный вызов Call MyInc(X) Debug.Print X End Sub
Вот результаты ее работы:
Хотя при первом вызове процедура нормально вызывается и увеличивает значение результата, но по завершении ее работы значение аргумента не изменяется. В этой ситуации не действует описатель ByRef, вызов идет так, будто параметр описан с описателем ByVal.
Если же процедура имеет более одного параметра, то попытка вызвать ее, заключив параметры в круглые скобки и не предварив этот вызов ключевым словом Call, приводит к синтаксической ошибке. Вот простой пример:
Public Sub SumXY(ByVal X As Integer, ByVal Y As Integer, ByRef Z As Integer) Z = X + Y End Sub Public Sub TestSumXY() Dim a As Integer, b As Integer, c As Integer a = 3: b = 5 'SumXY (a, b, c) 'Синтаксическая ошибка SumXY a, b, c Debug.Print c End Sub
В этом примере некорректный вызов процедуры SumXY будет обнаружен на этапе проверки синтаксиса.
Рассмотрим еще одну особенность вызова VBA процедур, связанную с аргументами, передаваемыми по ссылке. Как правило, в языках программирования для таких аргументов возможное значение фактического параметра ограничивается, — он должен быть именем переменной, ссылка на которую передается процедуре. VBA допускает возможность задания для таких аргументов констант и выражений в момент вызова. Все эти допущения делают язык менее надежным и чреваты серьезными ошибками. Именно поэтому мы останавливаем на них Ваше внимание.
Пусть, например, процедура CompVal c 4 аргументами, которая в зависимости от положительности z возвращает в переменной y либо увеличенное, либо уменьшенное на 100 значение x и сообщает об этом в строковой переменной w, определена следующим образом.
Sub CompVal(ByVal x As Single, ByRef y As Single, _ ByVal z As Integer, ByRef w As String) If z > 0 Then ' увеличение y = x + 100 w = "increase" Else ' уменьшение y = x - 100 w = "decrease" End If End Sub
Рассмотрим процедуру TestCompVal, в которой несколько раз вызывается процедура CompVal:
Sub TestCompVal() Dim a As Single Dim b As Single Dim n As Integer Dim S As String n = 5: a = 7.4 ' значения параметров CompVal a, b, n, S ' 1-ый вызов Debug.Print b, S CompVal 7.4, b, 5, S ' 2-ой вызов Debug.Print b, S CompVal 0, 0, 0, S ' 3-ий вызов Debug.Print b, S CompVal 0, 0, 0, "В чем дело?" ' 4-ый вызов Debug.Print b, S End Sub
В результате выполнения этой процедуры будут напечатаны следующие результаты:
107,4 increase 107,4 increase 107,4 decrease 107,4 decrease
Первые два вызова корректны. Следующие два вызова хотя и допустимы в языке VBA, но приводят к тому, что параметры, переданные по ссылке, не меняют своих значений в ходе выполнения процедуры и, по существу, вызов ByRef по умолчанию заменяется вызовом ByVal. Конечно, было бы лучше, если бы эта программа выдавала ошибки на этапе проверки синтаксиса.
Вызовы функций
Оформление вызова функции зависит от того, требуется ли использовать ее значение в вызывающей процедуре. Если Вы хотите передать вычисляемое функцией значение в переменную или применить его в выражении правой части оператора присвоения, то вызов пользовательской функции имеет тот же вид, что и вызов встроенной функции, например, sin(x). При вызове указывается имя функции, а после него идет заключенный в круглые скобки список фактических параметров. Например, если заголовок функции MyFunc:
Func Myfunc(Name As String, Age As Integer, Newdate As Date) As Integer
использовать ее значение можно с помощью вызовов:
val= Myfunc("Alex",25, "10/04/97")
или
x = sqrt(Myfunc("Alex",25, "10/04/97")) + x
Если же значение, вычисляемое функцией, нас не интересует и нужно воспользоваться лишь ее побочными эффектами, вызов функции может иметь ту же форму, что и вызов процедуры Sub. Например:
Myfunc "Alex",I, "10/04/97"
или:
Call Myfunc(Myson, 25, DateOfArrival)