Вы пишете определяемую пользователем функцию (UDF), то есть Public Function
в общедоступном стандартном модуле, которую можно вызывать из ячейки таблицы - и этот конкретный тип функции имеет определенный набор ограничений. Например, нельзя иметь побочных эффектов . UDF принимает некоторые входные данные, обрабатывает их, , а затем возвращает результат .
Таким образом, подпись UDF должна выглядеть следующим образом:
Public Function {name}({args}) As {type}
Самое первое, о чем нужно подумать, когда вы пишете функцию, это , что вам нужно, чтобы она возвратила - другими словами, , что должна делать ячейка с надписью =MYFUNCTION(A1,A2)
содержать после расчета . Например, если вы написали функцию Add
, которая добавляет два значения Double
, вы захотите, чтобы она возвращала Double
:
Public Function Add(ByVal value1 As Double, ByVal value2 As Double) As Double
Тело функции функции вычисляет результат из заданных параметров :
Dim result As Double
result = value1 + value2
Прежде чем он вернется / выйдет, вам нужно присвоить возвращаемое значение функции . Это делается путем присвоения идентификатору функции:
Add = result
Механизм вычислений Excel затем принимает этот результат, и вот как ячейка с формулой, такой как =Add(2, 2)
, заканчивается значением 4
.
Ваша функция STATUS
зависит от ActiveCell
, то есть от любой ячейки, выбранной в данный момент на ActiveSheet
: , она НЕ является ячейкой, вызвавшей функцию . Пересчет рабочей книги с выбранной случайной ячейкой, скорее всего, даст неверные результаты.
Будучи UDF, он не может .ClearContents
в ячейке (или влиять на любую другую ячейку каким-либо образом) - поэтому функция возвращает ошибку #NAME?
для пути выполнения, который входит в условный блок и так как возвращаемое значение никогда не назначается, другой путь выполнения выдает 0
, который является числовым представлением варианта Empty
, который в настоящее время возвращает ваша функция.
Если UDF необходимо знать о значении другой ячейки, лучше всего принять значение этой ячейки в качестве параметра: таким образом, функция работает без каких-либо предположений о компоновке рабочего листа. Насколько полезным было бы VLOOKUP
, если бы он не принимал аргумент lookup_value
и вместо этого брал это значение из ячейки .Offset(0, 1)
? Там будут бунты!
Когда вам нужно сделать что-то вместо вычислить / вычислить что-то , вам нужен не UDF, а макрос .
Макрос - это процедура без параметров Public Sub
в общедоступном стандартном модуле (или модуле Worksheet
), которую можно вызывать из окна «Макросы» или выполнять при нажатии пользователем Shape
, ActiveX CommandButton
, или они могут быть назначены свойству OnAction
какого-либо пользовательского пункта меню - что бы ни раскачивало вашу лодку.
Sub
процедуры делают что-то, они действия . Они могут получать доступ и изменять глобальное состояние, изменять любую ячейку, рабочую таблицу или рабочую книгу; они даже могут порождать экземпляр PowerPoint и вставлять диаграмму в виде картинки на новый Slide
- все, о чем вы только могли подумать, небо - предел!
Поскольку то, что вам нужно, это то, что делает вещи , код, который вам нужно написать, должен быть больше похож на макрос. Не называйте это STATUS
; используйте глагол и опишите, что он делает: вы перемещаете значения из одного столбца в другой, основываясь на заданных критериях. Когда вы пишете Sub
процедуру, подумайте сначала о том, как вы хотите вызвать it.
Я думаю, что-то вроде этого было бы аккуратно:
MoveValues Sheet1.Range("$B$2:$B$22"), "System"
Таким образом, подпись будет выглядеть так:
Private Sub MoveValues(ByVal Target As Range, ByVal Criteria As String)
И тело теперь может проходить через указанный диапазон Target
, оценивать, соответствует ли ячейка справа Criteria
, а затем соответственно перемещать значение влево. Или лучше - как насчет того, что мы вообще не принимаем макет листа и вызываем его так:
1114 * *
Теперь, если нам когда-нибудь понадобится вставить столбец между A и B или между B и C, нам нужно только изменить аргументы, которые мы передаем нашей процедуре, а не саму процедуру!
Private Sub MoveValues(ByVal Source As Range, ByVal Target As Range, ByVal Status As Range, ByVal Criteria As String)
Но сначала нам нужно проверить наши предположения и решить, что делать, если наши ожидания не оправдались - нам нужны диапазоны из одного столбца с равным количеством строк!
Во многих случаях лучше всего поднять ошибку во время выполнения. Ошибка № 5 «Недопустимый вызов процедуры или аргумент», кажется, достаточно хорошо подходит:
If Source.Columns.Count <> 1 Or Target.Columns.Count <> 1 Or Status.Columns.Count <> 1 Or _
Source.Rows.Count <> Target.Columns.Count Or _
Status.Rows.Count <> Target.Columns.Count _
Then
Err.Raise 5
End If
Мы даже можем настроить сообщение об ошибке, чтобы помочь нам отладить вызывающий код позже, когда мы изменим параметры через 6 месяцев и забыли все о предположениях этой MoveValues
процедуры:
If Source.Columns.Count <> 1 Or Target.Columns.Count <> 1 Or Status.Columns.Count <> 1 Or _
Source.Rows.Count <> Target.Columns.Count Or _
Status.Rows.Count <> Target.Columns.Count _
Then
Err.Raise 5, "MoveValues", _
"Source, Target, and Status ranges must be 1 column and the same number of rows."
End If
Нам также нужно убедиться, что наш Criteria
не пустой или просто пробел!
If Trim$(Criteria) = vbNullString Then
Err.Raise 5, "MoveValues", "Criteria string cannot be empty or whitespace."
End If
Теперь, когда мы проверили наши входные данные , в остальной части процедуры можно смело предположить, что диапазоны Source
, Target
и Status
представляют собой диапазоны из одного столбца, что они все одинакового размера, и у нас есть действующие критерии для работы. Итак, мы можем перейти к итерации ячеек и сделать свое дело:
Dim current As Long
For current = 1 To Target.Rows.Count
If Status.Cells(current).Value = Criteria Then
Target.Cells(current).Value = Source.Cells(current).Value
Source.Cells(current).ClearContents
End If
Next
Теперь осталось только написать макрос, который его вызывает:
Public Sub MoveSystemValues()
With Sheet1
MoveValues .Range("A2:A22"), .Range("B2:B22"), .Range("C2:C22"), "System"
End With
End Sub
И теперь мы можем запустить этот макрос MoveSystemValues
из окна Макросы в Excel или назначить MoveSystemValues
некоторой Shape
или кнопке ... и затем понять, что он работает довольно хорошо для небольшого количество рядов, но довольно медленное, учитывая большие диапазоны - но пока нам достаточно жевать.