2009-06-19 36 views
5

quiero tener/definir un identificador único para cada fila de datos en mi hoja de datos de Excel - de tal manera que lo puedo usar al pasar los datos en adelante y se mantiene la lo mismo cuando las filas se agregan/eliminan arriba.Como llegar/set id único para la celda en Excel mediante VBA

Mis pensamientos son utilizar el atributo ID de Rango (msdn link)

lo tanto, tengo una función definida por el usuario (UDF) que pongo en cada fila que Establece/Obtiene el ID de la siguiente manera:

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 

Pero esto falla en la línea mencionada con

"error definido por la aplicación o definido a objetos"

pensé que tal vez es una de esas limitaciones de la UDF, sino también consigo el mismo error si lo intento de código se activa con un botón de la cinta ...

¿Alguna otra sugerencia sobre cómo mantener los identificadores consistentes - tal vez Debería completar las celdas a través de mi botón de cinta, encontrar celdas sin ID y generar/establecer el valor de celda de esas ...

EDIT: Como pensé, tengo la hoja protegida, pero incluso en una celda desbloqueada todavía falla Desproteger la hoja soluciona el problema ... pero he usado "Proteger UserInterFaceOnly: = True", lo que debería permitirme hacer esto. Si permito manualmente "Editar objetos" cuando proteger la hoja también funciona, pero no veo una opción programática para eso - y necesito llamar a la función de protección en Autoopen para activar la función UserInterfaceOnly ...

Supongo que necesito desactivar/activar la protección alrededor de mi configuración de ID, suponiendo que se puede hacer en una UDF ... que parece que no puede, ya que eso no funciona, ni ActiveSheet.unprotect ni ActiveWorkbook.unprotect :(

Gracias de antemano Chris

+1

Su código funciona bien aquí en Excel 2003 y 2007. ¿Su libro no está bloqueado o algo así? – Ant

Respuesta

0

He descubierto que si puedo proteger a la hoja con "Protect DrawingObjects: = False", la UDF puede establecer el ID. Extraño.

Gracias por la ayuda con esto.

1

coinciden con Ant. - su código funciona bien aquí en Excel 2003 SP3

. 0

También he sido capaz de utilizar:

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 

Aha! Creo que lo tengo

Creo que estás llamando a esto desde una fórmula de matriz, y solo se llama UNA VEZ con todo el rango. No puede obtener una identificación para un rango, solo una celda. Esto explica por qué Application.Caller.ID falla para usted, porque el rango ("A1: B9"). ID genera un Application-defined or object-defined error.

Cuando usa Range(Application.Caller.Address) para obtener la "celda" simplemente difiere este error a la línea currCell.ID.

+0

No es una fórmula de rango, solo una celda. –

+0

Siguiendo su EDIT arriba, ¿ha intentado usar ActiveSheet.Unprotect o ActiveWorkbook.Unprotect? Siempre puede hacer referencia al Cell.Parent si desea evitar ser explícito con la hoja (un movimiento inteligente). Creo que debido a que el UDF es parte de la interfaz de usuario, al ser una fórmula, no se ha podido realizar ningún cambio en la hoja, pero es sólo una corazonada. –

1

Creo que podemos tener algunos problemas aquí, pero creo que están probando problemas, no problemas con el código en sí. Primero, si llama a la función desde cualquier otra cosa que no sea una Celda, como la ventana inmediata, otro código, etc. No se establecerá Application.Caller. Esto es lo que genera los errores de su objeto no encontrado. En segundo lugar, si copia/pega la celda que tiene la función, también copiarán/pegarán la ID. Entonces, donde sea que lo pegue, la salida seguirá siendo la misma. Pero si solo copia el texto (en lugar de la celda), y luego lo pega, funcionará bien. (Incluyendo su uso original de Application.Caller.)

+0

Gracias por la información sobre las funciones copiar/pegar de ID –

4

bien ...

sí parece que si la hoja está bloqueada, las macros no tienen acceso de escritura a la información de bajo nivel, tales como identificación.

Sin embargo, no creo que sea posible desproteger la hoja dentro de una UDF. Por diseño, las UDF están muy restringidas; Creo que tener una fórmula de celda que controle la protección de la hoja rompería el paradigma de la fórmula de que una fórmula de célula afecta solo a una célula. Consulte this page en el sitio web de Microsoft para obtener más información.

Creo que esto limita sus opciones. Debe:

  • renunciar a la protección de hoja
  • renunciar a la UDF, utilice un evento Worksheet_Change para captar los cambios celulares y escribir a Identificación hay
  • usar una UDF que escribe el identificador en el valor de la celda, en lugar de guardar en ID

El enfoque UDF está plagado de problemas, ya que está tratando de usar algo diseñado para el cálculo de una celda para hacer una marca permanente en la hoja.

Sin embargo, aquí hay un ejemplo de una UDF que puede usar para sellar un valor "permanente" en una celda, que funciona en las celdas desbloqueadas de una hoja protegida. Este solo funciona para celdas individuales (aunque podría ser adaptado para una fórmula de matriz).

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 

Esto es bastante defectuoso. Sin embargo, el uso de copy o fillbox retendrá el valor copiado previamente. Solo al establecer explícitamente celdas como una nueva fórmula, funcionará. Pero si ingresa nuevamente la fórmula en la celda (simplemente haga clic en ella, presione ENTRAR) se calcula un nuevo valor, que es el comportamiento estándar de la celda.

Creo que el evento Worksheet_Change es el camino a seguir, que tiene mucha más latitud. Aquí hay un ejemplo simple que actualiza el ID de cualquier cambio de celda. Se puede adaptar a su escenario particular. Esta función debería agregarse a cada hoja de cálculo en la que se requiera el comportamiento de configuración de 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 

Última nota; en todos los casos, su contador de ID se restablecerá si vuelve a abrir la hoja de trabajo (al menos bajo los detalles limitados presentados en su ejemplo).

Espero que esto ayude.

+2

Muchas gracias por este análisis detallado, pero he encontrado que si protejo la hoja con "Protect DrawingObjects: = False", la UDF puede establecer el Id. Extraño ... –

+0

Maldición, bien hecho en encontrar eso. ¿Estás completamente ordenado ahora o todavía hay problemas de simulación? –

1

El problema es con Application.Caller.

Como lo está llamando desde una función definida por el usuario, le pasará una descripción del error. Aquí está la observación en el archivo de Ayuda.

Observaciones

Esta propiedad devuelve información acerca de la forma visual se llama básico, como se muestra en la siguiente tabla.

de llamadas - Valor de retorno

  • Una función personalizada introducida en una sola célula - un objeto Range especificando que celular
  • una función personalizada que es parte de una fórmula de matriz en una gama de células - Una gama oponerse especificando que rango de celdas
  • Un Auto_Open, Auto_Close, Auto_activar, o macro Auto_Deactivate - el nombre del documento como texto
  • Una macro establece ya sea por el OnDoubleClick o OnEntry propiedad - el nombre del identificador de objeto de gráfico o célula árbitro nce (si corresponde) al que se aplica la macro
  • El cuadro de diálogo Macro (menú Herramientas), o cualquier persona que llama que no se describa anteriormente - El #REF! valor de error

Puesto que usted está llamando desde una función definida por el usuario, lo que está sucediendo es Application.Caller devuelve una cadena de un código de error a su alcance variable curcell. NO está causando un error que su manejador de errores recogería. Lo que sucede después de que hagas referencia a curCell, ya no es un rango. En mi máquina intenta configurar curCell = Range ("Error 2023"). Cualquiera que sea el objeto, es posible que ya no tenga un atributo de identificación y cuando intente establecerlo, le arrojará ese error de objeto.

Esto es lo que me gustaría probar ...

  1. Trate de no incluir el gestor de errores y ver si VBA arroja alguna excepción en el alcance (Application.Caller.Address). Esto no lo arreglará, pero podría apuntarlo en la dirección correcta.

  2. Ya sea a través de la lógica o Application.ActiveCell o como quiera hacerlo, haga referencia directamente a la celda. Por ejemplo Range ("A1") o Cells (1,1). Application.Caller.Address simplemente no parece ser una buena opción de usar.

  3. Pruebe usar la opción Explícito. Esto podría hacer que la línea en la que estableces curCell arroje un error ya que Range (Application.Caller.Address) no parece estar pasando un rango atrás, que es el tipo de datos de curCell.

Cuestiones relacionadas