4 votos

Error de ejecución de la función VBA de Excel 1004: Error definido por la aplicación o por el objeto

Estoy tratando de aprender funciones con el fin de simplificar y reutilizar el código siempre que sea necesario.

Empecé por convertir algo que utilizo con bastante frecuencia en una función: Devolver el valor entero de la última columna no vacía de una hoja de cálculo.

Function FindLastDataLine(strColName As String) As Long
    FindLastDataLine = Range(strColName).Offset(Rows.Count - 1, 0).End(xlUp).Row
End Function

Sub PracticeMacro()
    intItemCount = FindLastDataLine("A:A")
    MsgBox ("There are " & intItemCount & " rows of data in column A.")
End Sub

Cuando ejecuto esto recibo el error de ejecución '1004' "Application-defined or object-defined error" que útilmente se define como "la culpa de alguien más" para citar no muy textualmente.

¿En qué me estoy equivocando?

0 votos

Utilice siempre Option Explicit en la parte superior de su código, y declarar explícitamente las variables. No puedo decir lo vital que es esta práctica como programador.

0 votos

Gracias @Breakthrough. En los años transcurridos desde este comentario he tomado la costumbre de hacerlo.

4voto

Ravi Puntos 11

Por qué su método no funciona: hay dos razones aquí. La primera, cuando se pone Rows.Count no hay ninguna referencia para Rows - es una propiedad de un Range . Para solucionarlo, sólo hay que referenciar el mismo rango que ya está (sólo hay que añadirlo antes de llamar a Rows.Count así:

Function FindLastDataLine(strColName As String) As Long
    FindLastDataLine = Range(strColName).Offset(Range(strColName).Rows.Count - 1, 0).End(xlUp).Row
End Function

La segunda razón es que está utilizando Offset . Offset literalmente cambia un Range por lo mucho que lo cuentas. No quiere desplazar todo el rango de celdas, sino encontrar la última celda del rango. Puedes hacer esto de forma bastante sencilla cambiando Offset a Cells eliminando el Range() (ya que vamos a elegir una celda), y cambiando 0 a la columna que desee. Sin embargo, al pasar la columna como "A:A" no es posible, por lo que habría que sustituirlo por Range(strColName).Column de la siguiente manera:

Function FindLastDataLine(strColName As String) As Long
    FindLastDataLine = Cells(Range(strColName).Rows.Count, Range(strColName).Column).End(xlUp).Row
End Function

Una solución mejor: la siguiente solución funcionará en todas las versiones recientes de MS Office (2003, 2007 y 2010), y manejará los errores. Se llama pasando la letra de la columna, o el número de la columna:

Function GetLastDataRow(col As Variant) As Long
    GetLastDataRow = -1
    If (IsNumeric(col) And col >= 1) Or Len(col) <= 2 Then
        On Error Resume Next
        GetLastDataRow = _
            Cells(Cells(1, col).EntireColumn.Rows.Count, col).End(xlUp).Row
        On Error GoTo 0
    End If
End Function

A continuación se ilustra cómo se llamaría a esta función, y algunas salidas de ejemplo. Supongamos que toda la hoja está limpia, excepto por algunos datos aleatorios introducidos en las celdas B1 a B8 y B10 ( B9 se deja en blanco). Tenga en cuenta que no se introduce la columna como un rango, sino la letra de la columna o el número de columna (los valores no válidos devuelven -1):

GetLastDataRow(1)         =  1    GetLastDataRow("A")       =  1
GetLastDataRow(2)         = 10    GetLastDataRow("B")       = 10
GetLastDataRow("AX")      =  1    GetLastDataRow("A:X")     = -1
GetLastDataRow("Oops...") = -1    GetLastDataRow(200)       =  1

Como nota técnica, si el Cells falla, se asume que la entrada no es válida, por lo que la función devuelve -1. Le insto a que utilice esta práctica (devolver valores no válidos si la entrada era inválida) en su función, le ayudará mucho a evitar errores en el futuro.

Cómo funciona esto, encuentra la última fila posible en cualquier columna particular (depende de su versión de MS Office), y luego utiliza el End para encontrar la última celda de esa columna con datos.


Aquí hay una versión alternativa que devolverá 0 si todas las celdas de esa columna están en blanco:

Function GetLastDataRow(col As Variant) As Long
    GetLastDataRow = -1
    If (IsNumeric(col) And col >= 1) Or Len(col) <= 2 Then
        On Error Resume Next
        If IsEmpty(Cells(Cells(1, col).EntireColumn.Rows.Count, col).End(xlUp).Value) Then
            GetLastDataRow = 0
        Else
            GetLastDataRow = _
                Cells(Cells(1, col).EntireColumn.Rows.Count, col).End(xlUp).Row
        End If
        On Error GoTo 0
    End If
End Function

Ejemplo de salida:

GetLastDataRow(1)         =  0    GetLastDataRow("A")       =  0
GetLastDataRow(2)         = 10    GetLastDataRow("B")       = 10
GetLastDataRow("AX")      =  0    GetLastDataRow("A:X")     = -1
GetLastDataRow("Oops...") = -1    GetLastDataRow(200)       =  0

0 votos

Vale, siento que haya tardado tanto en volver a hablar de esto, pero ahí va. Una pregunta: Refiriéndome a la primera parte de tu respuesta, ¿si pasara FindLastDataLine("A:1") el sistema actual funcionaría porque está especificando la celda uno con la que Row puede trabajar en lugar de un rango? Esto se centra sólo en la primera parte para que pueda entender mejor su sugerencia.

0 votos

Lo siento, no entiendo bien su pregunta... "A:1" por sí mismo no es un rango válido. Antes de la función personalizada que escribí, es necesario pasar un rango válido como una cadena (es decir A:A , A2:A15 etc...).

0 votos

Vale, ya lo tengo. Gracias por esta minuciosa descripción del problema y la solución.

1voto

Michael Bishop Puntos 148

Has escrito "Devuelve el valor entero de la última fila no en blanco de una hoja de cálculo". Pero parece que estás tratando de obtener la última fila no en blanco de una columna concreta de una hoja de cálculo.

Si cambias tu función por esta, creo que funcionará:

Function FindLastDataLine(strColName As String) As Long
    FindLastDataLine = ActiveSheet.Cells(ActiveSheet.Rows.Count, strColName).End(xlUp).Row
End Function

1 votos

Yeap... parece que el tema dice una cosa, los nombres de las funciones otra... y el código en sí otra cosa.

0 votos

Gracias, eso me hizo reír. Creo que ese comentario podría aplicarse a muchas preguntas.

0 votos

Lo siento, efectivamente estaba buscando el valor entero de la última celda no negra de una columna, no de una fila. Es culpa mía.

1voto

George Puntos 90

Puede que no solucione su error, pero devolvería el ItemCount dentro del rango...

Function FindLastDataLine(strColName As String) As Long
    FindLastDataLine = Evaluate("COUNTA(" & strColName & ")")
End Function

Sub PracticeMacro()
    intItemCount = FindLastDataLine("A:A")
    MsgBox ("There are " & intItemCount & " rows of data in column A.")
End Sub

Observa que hay otras formas de llamar a esta función COUNTA de Excel...

0 votos

Esto no tiene en cuenta las columnas en blanco, por lo que ni siquiera se acerca a la idea del cartel original de "devolver el valor entero de la última fila no en blanco de una hoja de cálculo".

0 votos

Mira lo que dice el cuadro de mensajes... MsgBox ("There are " & intItemCount & " rows of data in column A.") . AFAIK, COUNTA hace exactamente esto. Echa un vistazo a mi comentario al post de @PerryJ.

1 votos

Lo siento, no lo había visto - buena decisión. Me encanta la gente que utiliza nombres terriblemente ambiguos en su código.

0voto

InquilineKea Puntos 460

Inténtalo:

FindLastDataLine = _
     Range(strColName).Offset(ActiveSheet.Rows.Count - 1, 0).End(xlUp).Row

Rows es una propiedad.

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by: