Хорошо ...
Похоже, что если лист заблокирован, у макросов нет доступа на запись к низкоуровневой информации, такой как идентификатор.
Однако я не думаю, что можно снять защиту листа в UDF. По своей конструкции UDF сильно ограничены; Я думаю, что наличие формулы ячейки, контролирующей защиту листа, нарушило бы парадигму формулы, согласно которой формула ячейки влияет только на ячейку.
Подробнее см. на этой странице на веб-сайте Microsoft.
Я думаю, что это ограничивает ваши возможности. Вы должны либо:
- отказаться от защиты листа
- отказаться от UDF, использовать событие Worksheet_Change, чтобы зафиксировать изменения в ячейке и записать туда ID *
- использовать UDF, который записывает идентификатор в значение ячейки, а не сохранять в ID
Подход UDF чреват проблемами, так как вы пытаетесь использовать что-то, предназначенное для вычисления ячейки, чтобы сделать постоянную отметку на листе.
Тем не менее, вот пример UDF, который вы можете использовать, чтобы пометить «постоянное» значение в ячейке, которое работает на разблокированных ячейках защищенного листа. Этот работает только для отдельных ячеек (хотя он может быть адаптирован для формулы массива).
Public Function CellMark()
Dim currCell As Range
Set currCell = Range(Application.Caller.Address)
Dim myId As String
' must be text; using .value will cause the formula to be called again
' and create a circular reference
myId = currCell.Text
If (Trim(myId) = "" Or Trim(myId) = "0") Then
myId = "ID-" & Format(CStr(gNextUniqueId), "00000")
gNextUniqueId = gNextUniqueId + 1
End If
CellMark = myId
End Function
Это довольно некорректно. Использование копирования или поля заполнения, однако, сохранит предыдущее скопированное значение. Только явно установив ячейки в качестве новой формулы, она будет работать. Но если вы снова введете формулу в ячейку (просто щелкните ее, нажмите ВВОД), будет вычислено новое значение - стандартное поведение ячейки.
Я думаю, что событие Worksheet_Change - это путь, который имеет гораздо большую широту. Вот простой пример, который обновляет идентификатор любых изменений ячейки. Это может быть адаптировано к вашему конкретному сценарию. Эту функцию необходимо будет добавить к каждому рабочему листу, для которого требуется поведение настройки идентификатора.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim currCell As Range
Set currCell = Target.Cells(1, 1)
Dim currId As String
currId = currCell.ID
If Trim(currCell.ID) = "" Then
Target.Parent.Unprotect
currCell.ID = CStr(gNextUniqueId)
Target.Parent.Protect
gNextUniqueId = gNextUniqueId + 1
End If
End Sub
Последнее примечание; во всех случаях ваш счетчик идентификаторов будет сброшен, если вы снова откроете лист (по крайней мере, в соответствии с ограниченными подробностями, представленными в вашем примере).
Надеюсь, это поможет.