我希望为我的Excel数据表中的每个数据行设置/定义一个唯一的ID - 这样我可以在向前传递数据时使用它,并在上面添加/删除行时保持不变。
我的想法是使用Range(msdn link)
的ID属性所以,我有一个用户定义的函数(UDF),我在每一行中放置/设置ID,如下所示:
Dim gNextUniqueId As Integer
Public Function rbGetId(ticker As String)
On Error GoTo rbGetId_Error
Dim currCell As Range
'tried using Application.Caller direct, but gives same error
Set currCell = Range(Application.Caller.Address)
If currCell.id = "" Then
gNextUniqueId = gNextUniqueId + 1
'this line fails no matter what value I set it to.
currCell.id = Str(gNextUniqueId)
End If
rbGetId = ticker & currCell.id
Exit Function
rbGetId_Error:
rbGetId = "!ERROR:" & Err.Description
End Function
但是在
提到的那一行失败了“应用程序定义或对象定义的错误”
我认为这可能是UDF的限制之一,但如果我从功能区按钮触发的代码中尝试它,我也会得到同样的错误......
关于如何保持一致id的任何其他建议 - 也许我应该通过我的功能区按钮填充单元格,找到没有ID的单元格并生成/设置那些单元格的值...
编辑: 正如Ant所想,我保护了表单,但即使在未锁定的单元格中,它仍然会失败。取消保护工作表修复了问题....但我使用了“保护UserInterFaceOnly:= True”这应该允许我这样做。如果我在保护工作表时手动允许“编辑对象”它也可以工作,但是我没有看到编程选项 - 我需要调用AutoOpen中的Protect功能来启用UserInterfaceOnly功能......
我想我需要在我的ID设置周围关闭/打开保护 - 假设可以在UDF中完成...它似乎不能,因为它不起作用 - 既不是ActiveSheet.unprotect也不是ActiveWorkbook.unprotect: (
提前致谢。 克里斯
答案 0 :(得分:4)
好了...
如果工作表被锁定,宏似乎没有对ID等低级信息的写访问权。
但是,我不认为可以在UDF中取消保护表单。根据设计,UDF受到严格限制;我认为使用细胞配方控制片材保护会破坏细胞配方仅影响细胞的配方范例。 有关详细信息,请参阅Microsoft网站上的this page。
我认为这会限制您的选择。你必须:
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
但这是非常有缺陷的。但是,使用副本或填充框将保留先前复制的值。只有明确地将单元格设置为新公式才有效。但是如果再次将公式输入到单元格中(只需单击它,按Enter键),就会计算出一个新值 - 这是标准的单元格行为。
我认为Worksheet_Change事件是要走的路,它有更多的自由度。这是一个更新任何单元格更改ID的简单示例。它可以根据您的特定情况进行定制。需要将此函数添加到每个工作表中,需要ID设置行为。
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
最后注意;在所有情况下,如果您重新打开工作表,将重置您的ID计数器(至少在您的示例中提供的有限详细信息下)。
希望这有帮助。
答案 1 :(得分:1)
与Ant一致 - 您的代码在Excel 2003 SP3上运行正常。
我也可以使用:
Set currCell = Application.Caller
If Application.Caller.ID = "" Then
gNextUniqueId = gNextUniqueId + 1
'this line fails no matter what value I set it to.
currCell.ID = Str(gNextUniqueId)
End If
啊哈!我想我拥有它。
我认为你是从一个数组公式中调用它,它只会被称为ONCE的全范围。您无法获取范围的ID - 只有一个单元格。这解释了为什么Application.Caller.ID失败了,因为Range(“A1:B9”)。ID生成Application-defined or object-defined error
。
当您使用Range(Application.Caller.Address)
获取“单元格”时,您只需将此错误推迟到currCell.ID
行。
答案 2 :(得分:1)
我认为我们可能会遇到一些问题,但我认为他们正在测试问题,而不是代码本身的问题。首先,如果从Cell以外的任何其他函数调用该函数,如立即窗口,其他代码等,将不会设置Application.Caller。这就是生成对象未找到错误的原因。其次,如果您复制/粘贴具有该功能的单元格,您也可以复制/粘贴该ID。因此无论您将其粘贴到何处,输出都将保持不变。但是,如果你只是复制文本(而不是单元格),然后粘贴然后这将工作正常。 (包括您对Application.Caller的原始使用。)
答案 3 :(得分:1)
问题在于Application.Caller。
由于您是从用户定义的函数调用它,因此它会向您传递错误说明。以下是帮助文件中的注释。
备注
此属性返回有关如何调用Visual Basic的信息,如下表所示。
来电者 - 返回值
由于您是从用户定义的函数调用它,所以发生的事情是Application.Caller将错误代码的String返回到您的范围变量curCell。它不会导致错误处理程序拾取的错误。之后会发生什么,你引用了curCell,它实际上不再是一个范围了。在我的机器上,它尝试设置curCell = Range(“Error 2023”)。无论该对象是什么,它可能不再具有ID属性,当您尝试设置它时,它会抛出该对象错误。
这是我会尝试的......
尝试删除错误处理程序,看看VBA是否会抛出Range(Application.Caller.Address)上的任何异常。这不会解决它,但它可以指向正确的方向。
无论是通过逻辑还是Application.ActiveCell,或者您想要这样做,请直接引用单元格。例如Range(“A1”)或Cells(1,1)。 Application.Caller.Address似乎不是一个很好的选择。
尝试使用Option Explicit。这可能会使你设置curCell的行抛出一个错误,因为Range(Application.Caller.Address)看起来不像是传回一个范围,这是curCell的数据类型。
答案 4 :(得分:0)
我发现如果用“Protect DrawingObjects:= False”保护工作表,UDF可以设置Id。奇怪。
感谢您提供的所有帮助。