¿Por qué no puedo borrar un rango?

¡Hola Amigo Prozac!

Deseo te encuentres bien.

Ya hace tiempo que te consulté para que me ayudaras a crear una macro que me filtrara algunos datos. Espero te acuerdes.

Ahora bien, a esa macro le he agregado unos códigos más (tal vez no de forma muy coherente, o como me has dicho "muy liados"), sin embargo, mi problema es que no puedo borrar todo el rango de la filtración. Trataré de darme a entender:

En un módulo me dijiste que insertara el siguiente código:

Sub FiltraCelda(target As Range, ro As Range, rd As Range)
Dim c As Range
Dim i As Integer
i = 0
For Each c In ro
If InStr(UCase(c), UCase(target)) > 0 Then
rd.Offset(i, 0) = c
i = i + 1
End If
Next c
End Sub
Function CalculaRangoOrigen(rini As Range) As Range
If rini.Offset(1, 0) = "" Then
Set CalculaRangoOrigen = rini
Else
Set CalculaRangoOrigen = Range(rini, rini.End(xlDown))
End If
End Function
Sub BorraFiltro(c As Range)
c = ""
End Sub

Y en la hoja "PEDIDO" coloqué este otro:

Private Sub WorkSheet_Change(ByVal target As Range)
On Error Resume Next
For i = 12 To 91
Application.EnableEvents = True
If Not Application.Intersect(target, Range("J" & i)) Is Nothing Then
If Range("J" & i) = "" Then
Call BorraFiltro(CalculaRangoOrigen(Range("B" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("H" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("T" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("Z" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("AB" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("AD" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("AP" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("AR" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("AX" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BD" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BJ" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BL" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BN" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BP" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BR" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BT" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("BZ" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CE" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CI" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CK" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CM" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CO" & i)))
Call BorraFiltro(CalculaRangoOrigen(Range("CS" & i)))
Else
Call FiltraCelda(Range("J" & i), CalculaRangoOrigen(Worksheets("BASE DE DATOS").Range("E3")), Range("AD" & i))
End If
End If
Application.EnableEvents = False
Next i

Si notas Prozac, uso la macro "BorraFiltro" para borrar los datos arrojados por la filtración y funciones ConsultaV en cada una de las columnas ("B", "H", "T", "Z", etc.). Así pues, ¿cómo puedo hacerle para que la macro "BorraFiltro" me permita borrar en una sola acción o con un solo rango todos los datos de la filtración y las ConsultasV? Y es que si borro tal como lo tengo ahorita, primero se me borran los datos de la columna "B", luego los datos de la columna "H" y así sucesivamente hasta llegar a la última columna, la "CS".

Intenté hacerlo de la siguiente forma pero no me funcionó:

Call BorraFiltro(CalculaRangoOrigen(Range("B" & i & ":" & "CS" & i)))

¿Por qué no puedo borrar éste rango?

Bueno Prozac, gracias por la ayuda que siempre me has dado.

P. D. Adjunto link de descarga http://www.mediafire.com/download.php?1apymq7qqp83e9s

1 Respuesta

Respuesta
1

Lamentablemente no recuerdo todo, ni siquiera la mayoría de lo que hago en el foro. El código que en su día te dí, tendría algún propósito específico y por lo que veo está diseñado para trabajar con rangos que en realidad son celdas y columnas.

Por ejemplo Range("a3:b5"). Offset(4, 4) = 3 pone el valor 3 en las 6 celdas del rango, pero

If Range("a3:b5").Offset(4, 4) = 3 Then da un error porque no sabe que valor de los 6 o los 6 comparar.

Así la función BorraFiltro te funciona para cualquier rango, mientas que CalculaRangoOrigen no, porque hace ese tipo de comparación. Una posibilidad es cambiar dentro de CalculaRangoOrigen a algo como:

If rini.Offset(1, 0).Cells(0).Value = "" Then

Si solo quieres comparar la primera celda. Si necesitas comparar todas, tendría que ser un bucle for each.

Si lo que quieres es borrar un rango completo simplemente pon:

Range("B12:CS91") =""

Y no necesitas ni el bucle ni todas las demás líneas. Si mantienes el bucle para comprobar la intersección te valdría

Range("B" & i & ":" & "CS" & i)=""

Aunque si te fijas, estarás borrando varias veces las mismas celdas cuando i <91. Es más claro y eficiente que busques primero qué tienes que borrar exactamente (el valor de i) y luego lo borres de un golpe.

¡Buenas Prozac!

Es correcto lo que me dijiste. Cambié dentro de la función CalculaRangoOrigen la instrucción:

If rini.Offset(1, 0).Cells(0).Value = "" Then

Con ello, solo logré borrar la primera fila, pero se me trabó Excel, dejó de funcionar. Sin embargo, eso es lo que precisamente quiero (me refiero a borrar), pero en todas la filas del rango que arroje la filtración. Me dijiste que lo puedo lograr mediante un bucle for each, solo que no sé como plasmarlo, ¿cómo es eso Prozac dentro de la función CalculaRangoOrigen?

Gracias por toda la ayuda prestada Prozac.

Saludos.

Habría que ver que hizo que se trabara Excel, como tu dices. Puede ser un error en código, o algo así.

No obstante te pongo un sencillo ejemplo que borra todas las celdas del rango.

Sub CeldaxCelda()
Dim r As Range
Dim c As Range
For Each c In r
c.Value = ""
End Sub

Es equivalente a r = "" pero con un bucle. Te indicaba esto para las comprobaciones, puesto que las asignaciones se puede hacer así, pero no las comprobaciones. Como te decía.

R = "" es válido y borra el contenido de todas las celdas de un rago

IF r = "" da un error porque no sabe cual celda comparar. Si para que la condición sea válida han de ser todas, tendrás que usar un bucle. como el del ejemplo.

¡Prozac!

Ante todo gracias por tu respuesta.

Quiero decirte que en estos días que no te escribí, traté de entender tu respuesta y aplicarla, sin embargo, ya  me desesperé, no doy con la solución.

¿En qué y por qué estoy mal? Esto es lo que hice:

Function CalculaRangoOrigen(rini As Range) As Range
Dim r As Range
Dim c As Range
For Each c In r
c.Value = ""
 r = CalculaRangoOrigen
Set CalculaRangoOrigen = rini
Set CalculaRangoOrigen = Range(rini, rini.End(xlDown))
Next c
End Function

Espero no hacer perder tú paciencia. Gracias por todo.

Estaré al pendiente de tu respuesta.

Te estás liando con el nombre de la función y lo que devuelve. Para evitar estos problemas recomiendo usar el nombre de la función solo al final porque es como en VBA se devuelve el valor. En tu caso intentas devolver un valor con cada interacción del bucle for, lo cual no sé que efecto tendrá. Puede que esté llamando recursivamente a la función o que intente devolver varios valores. No estoy seguro que las llamadas sin parámetros se interpreten como devolución de valor siempre. En todo caso no es correcto.

Mira un ejemplo sencillo de como usar funciones, que normalmente se usan para devolver valores. Si solo hacen cosas sin devolver valores es mejor llamar a procedimientos (que no devuelven valores).

Function MiFuncion (x as Integer) as Integer
'Suma 2 y devuelve el resultado
Dim z as Integer
z = x + 2
MiFuncion = z
end Function

Se puede hacer lo mismo con una sola instrucción sin usar la variable z como:

Function MiFuncion (x as Integer) as Integer
'Suma 2 devuelve el resultado
MiFunction = x + 2
end Function

Y puedes decir, bueno y ¿por qué no hacerlo así? Bien, se puede y no hay problema, pero si la función se complica puede no ser una buena idea. En el ejemplo si la función suma al valor de entrada dos varias veces, tantas como indicadas con un segundo parámetro n, quedaría:

Function MiFuncion (x as Integer, n as Integer) as Integer
Dim z as Integer
Dim i as Integer
z = 0
For i=1 To n
    z = z + (x + 2) 'no hacen falta los paréntesis, es solo por claridad
Next i
MiFuncion = z
end Function

Si aplicas el mismo procedimiento al segundo método te dará un error o saldrán datos absurdos, porque estarías llamando a

 MiFunción = MiFuncion + (x +2)

La primera parte es para devolver el valor pero la segunda ¿Vuelve a llamar a la función o devuelve valor? Yo no lo sé así que mejor escribir claro.

Por otro lado no es exactamente lo mismo asignar con Set que sin Set. Set asigna objetos, mientras que = solo variables. No sé qué necesitas porque desconozco que quieres que haga la función. La función toma un rango y devuelve otro. La pregunta es ¿qué rango debe devolver en función del rango de entrada? Una posibilidad es por ejemplo que el rango de entrada siempre es una única celda y lo que se devuelve es el resto de la columna hasta la última celda no vacía.

Function CalculaRangoOrigen (rini As Range) As Range
'No se comprueba que rini sea una única celda pero siempre es así
Dim r as Range
Set r = Range(rini, rini.End(xldown))
set CalculaRangoOrigen = r
end Function
<br class="pln">

Y para probar la función

Sub Main ()
dim r as Range
set r = CalculaRangoOrigen(Range("A1"))
r = "hola"
end sub

Observa que cuando asigno los rangos uso Set, mientras que cuando uso = solo pongo el valor de todas las celdas del rango, en este caso a "hola". Este ejemplo hace que todas las celdas consecutivas no vacías desde A1 hacia abajo queden con el valor "hola". El ejemplo también funciona llamando a la función con "A1:A3" o incluso con "A1:B3", pero esto en general dependerá de lo que haga la función con el rango de entrada.

¡Amigo Prozac!

¡Wao! Excelente explicación.

Ahora entiendo de forma más clara cómo realizan su trabajo las funciones. Es evidente tu conocimiento y experiencia.

Ahora bien, quiero decirte que ya logré que la función "CalculaRangoOrigen" me permita incluir un rango, solo que cuando lo mando borrar, excel se traba, deja de funcionar.

Este es lo que puse en el módulo:

Function CalculaRangoOrigen(rini As Range) As Range
Dim r As Range
Dim c As Range
Set r = Range(rini, rini.End(xlDown))
Set CalculaRangoOrigen = r
    For Each c In r
        rini.Offset(1, 0) = c
        c = ""
    Next c
End Function
Sub BorraFiltro(c As Range)
    c.Value = ""
End Sub

<span> </span>  

Y en la hoja "PEDIDO" puse ésta otra instrucción:

Call BorraFiltro(CalculaRangoOrigen(Range("B" & i & ":" & "CS" & i)))

<span> </span> 

Prozac, ¿por qué es que se me traba excel y deja de funcionar, aunque si se me borra todo el rango cuando lo llamo con la función "BorraFiltro"? ¿en qué estoy mal nuevamente?

Intensamente agradecido estoy para contigo por todo tui apoyo.?

He puesto tu código en un procedimiento (con i =1 y unas cuantas celdas rellenas)y funciona, aunque no estoy seguro que hace lo que debe y desde luego cómo lo debe.

Nada que objetar a la llamada, ni a la función BorraFiltro. Sin embargo lo que hace CalculaRangoOrigen es un caos. Te explico paso a paso.:

Set r = Range(rini, rini.End(xlDown))

Hace que el rango r sea igual a la celda inicial mas todas las que no estén vacías hacia abajo con la longitud de la primera columna. Con tu llamada, si suponemos que i=1, y la primera celda vacía de la columna B es la 15, el resultado es que r es B1:CS14.

Set CalculaRangoOrigen = r

Esto hace que r sea lo que devuelve la función. Por tanto lo que se haga después sobre el rango r dentro de la función no tendrá efecto si como es el caso se borra r. En otras palabras, si tu función termina aquí, que es lo lógico, al llamar a BorraFiltro con r, se borrarán las celdas del rango r. Siempre se pone al final lo que devuelve la función, porque se entiende que una función se hace para devolver un valor, un objeto... Una vez terminados los cálculos para saber que devolver no tiene sentido que la función haga nada más, salvo tal vez actualizar una condición de error o algo así, lo que no es tu caso.

Ahora incluyes el siguiente bucle, que en mi opinión te sobra.

For Each c In r
 rini.Offset(1, 0) = c
 c = ""
 Next c

Este bucle hace que para cada celda del rango r, copias la celda en la fila siguiente tantas veces como celdas tengas. y luego borras la celda

En el ejemplo que te he puesto, r= B1:CS14 . Con este rango y el bucle estás haciendo

copiar el valor de B1 en toda la fila B2:CS2 y luego borras B1

B2:CS2 = C1 y luego borras C1

B2:CS2 = D1 y luego borras D1

Y así hasta CS14.

En resumen, y con los datos de mi ejemplo escribes la segunda fila del rango 1344 veces y tras cada vez borras la celda que originó la escritura.

¡Prozac!

Todo lo que me has enseñado hasta el día de hoy, me ha permitido entender de forma más clara la macro que en su día me diste, lo cual, estoy super agradecido contigo. ¡Eso es genial!

Sin embargo, como me dijiste al inicio de tu anterior respuesta: el código aun no "hace lo que debe y desde luego cómo lo debe".

Espero darme a entender:

Resulta que si tú tecleas en la celda "J12" de la hoja "PEDIDO" las letras "MC DOMI" y luego das clic en la lista desplegable, verás que se filtran:

MC DOMINICANA
MC DOMINO - CH C/CRIS
MC DOMINO - CH S/CRIS
MC DOMINO - GR C/CRIS
MC DOMINO - GR S/CRIS


Pues estas palabras empiezan con las letras "MC DOMI". Entonces, seleccionamos "MC DOMINO - CH C/CRIS" o cualquiera, y el filtro me arrojará en la celda:


"AD12"    MC DOMINO - CH C/CRIS - 1 DE 4
"AD13"    MC DOMINO - CH C/CRIS - 2 DE 4
"AD14"    MC DOMINO - CH C/CRIS - 3 DE 4
"AD15"    MC DOMINO - CH C/CRIS - 4 DE 4

Hasta aquí todo va bien. El problema se me presenta a partir del segundo filtro. Por ejemplo, si escribimos nuevamente pero ahora en la celda "J16" las letras "MC CAR", y das clic en la lista desplegable, veras que se filtran tres mesas de centro:


MC CARABELA
MC CARRARA
MC CARTAGENA

Escogemos la "MC CARTAGENA" o cualquiera, y nuevamente el resultado de la filtración me arrojará en la celda:

"AD16"    MC CARTAGENA - 1 DE 4
"AD17"    MC CARTAGENA - 2 DE 4
"AD18"    MC CARTAGENA - 3 DE 4
"AD19"    MC CARTAGENA - 4 DE 4

Ahora bien, valiéndome de los datos que fueron arrojados por la filtración en el rango ("AD12:AD19), realizo un "CONSULTAV" de éstos, cuyos datos arrojados por la "CONSULTAV" son puestos en las columnas: "AP", "AR", "AX" .... hasta la columna "CS".

Si me he dado ha entender hasta este punto, mi problema se presenta al borrar, pues, si borro las palabras "MC CARTAGENA" de la celda "J16", la función "BorraFiltro" lo hace desde la fila "12:19" y no de la "16:19" que es como debería. En otras palabras, lo que necesito que se me borren por separado los datos de cada filtración.

Nota: Nunca voy a empezar a borrar los rangos de arriba hacia abajo, sino de abajo hacia arriba, es decir, primero borraría la última celda que fue la "J16" y se me debería borrar el rango desde "B16:CS19" y luego la celda "J12" y se me debería borrar el rango "B12:CS15".

¿Por qué no puedo lograr ésto Prozac? ¿Se podrá?

Espero aun no haberte impacientado.

Y como gsiempre gte he dicho, gracias por todo tu apoyo intelectual.

P. D. Adjunto link de descarga del archivo en el que estoy trabajando  actualmente por si deseas darle un vistazo http://www.mediafire.com/download.php?1apymq7qqp83e9s.

¡Hola Prozac!

Cómo vamos, ¿creés que si se va a poder?

Estaré al pendiente de pronta respuesta.

Saludos.

Siento tardar en responder pero espero que comprendas que mi tiempo principal está para mi familia y mi trabajo. Trato de ser rápido pero demás tengo que dividir el tiempo entre quienes me preguntáis.

He visto tu código y confieso que me resulta muy enrevesado de mantener. Creo que has hecho un esfuerzo ímprobo, aunque puede que un diseño inicial de tu estructura de datos diferente hubiera facilitado las cosas.

Yendo al tema. En tu código siempre llamas la función de filtrado dentro de un bucle for que empieza por i=12, por lo tanto siempre te empieza por ahí. Si necesitas que comience por otra fila en alguna parte tendrás que guardar la información. Puede ser variables del propio programa o celdas de una hoja de datos. Te recomiendo esto último sobre todo para que veas como funciona tu programa.

Supongamos entonces que generas una hoja de datos nueva llamada por ejemplo: "Log" por decir algo. En ella escribes esta cabecera, entre paréntesis las columnas

Fila 1: (A) filtro, (B) selección, (C) fila_ini,

Y cuando filtras escribes una fila rellenando los datos por ejemplo:

Fila 2: MC DOMI, MC DOMINO - CH C/CRIS, 12

Fila 3: MC CAR, MC CARTAGENA, 16

Cuando filtras escribes en la hoja Log y cuando quieras borrar lees la última fila, borras los datos del filtro y borras esa fila también en la hoja Log.

Tienes que ver como distingues cuando el dato se escribió manualmente de cuando procede de la selección de la lista de validación. Tal vez por la longitud del dato o por el resultado de la filtración. O si no encuentras una manera, elimina la columna B de la hoja Log. En ese caso escribirás dos veces 12 en dos filas diferentes, y la hora de borrar tendrás que hacer lo propio.

Fila 1: (A) filtro, (B) fila_ini,
Y ahora cuando filtras escribes una fila rellenando los datos por ejemplo:
Fila 2: MC DOMI, 12

Fila 2MC DOMINO - CH C/CRIS, 12
Fila 3: MC CAR, 16

Fila 4: MC CARTAGENA, 16

En cualquier caso haz (dentro del módulo 1) una función/procedimiento para escribir, otra para borrar y llámalas desde donde proceda. Para el último ejemplo serían:

Sub EscribeLog(texto As String, fila As Integer)
'Escribe una fiila en el log
Dim maxi As Integer
With Worksheets("Log")
 maxi = .Range("A65500").End(xlUp).Row + 1 'primera fila vacía
 .Range("A" & maxi).Value = texto
 .Range("B" & maxi).Value = fila
End With
End Sub
Function BorraLog() As Integer
'Borra las filas que hagan referencia al mismo filtro/nº de fila
Dim maxi As Integer
Dim f As Integer
With Worksheets("Log")
 maxi = .Range("A65500").End(xlUp).Row 'ultima fila llena
 f = .Range("B" & maxi).Value
 Do
 .Range("A" & maxi).Value = ""
 .Range("B" & maxi).Value = ""
 maxi = maxi - 1
 Loop While (.Range("B" & maxi).Value = f)
End With
End Function

Por otro lado creo que tienes cambiados las condiciones de activación de eventos. Cuando tu hoja cambia debes desactivar los eventos para que los cambios provocados por el filtrado no provoquen más comprobaciones. Al menos es lo habitual.

Application.EnableEvents = False
... aqui tu código
.. y al final volvemos a dejar activos los eventos
Application.EnableEvents = True

Por cierto, no he copiado bien la función justo antes del final falta el número de fila a devolver para que lo puedas usar luego en el filtro.

...
BorrarLog = f
end Function

Perdón por el error.

¡Hola Prozac!

Esta vez si que fué profundo.

Honestamente casi no entiendo el código que me diste, y más aún, no sé cómo colocarlo dentro del código que ya tengo (he tratado de hacerlo pero no doy).

¿Podrías decirme por favor cómo hacerlo?

Gracias y saludos.

¡Hola Prozac!


Esta vez si que fué profundo.


Honestamente casi no entiendo el código que me diste, y más aún, no sé cómo colocarlo dentro del código que ya tengo (he tratado de hacerlo pero no doy).


¿Podrías decirme por favor cómo colocarlo dentro del código que ya tengo?


Gracias y saludos.

Vamos a ver. El procedimiento y la función van en el módulo 1. Cada vez que haces un filtrado (justo después) llamas al procedimiento EscribeLog pasándole el string (el filtro) y la fila en la que estás en el formulario. Ello hace que cada vez que se filtre se escriba en la hoja Log lo que se hizo. Debido a tu diseño, para mi enrevesado y complicado de mantener, a priori no sabes cuando estás escribiendo en la celda para tener un desplegable con menos elementos y cuando porque ya escogiste un elemento concreto. El programa solo sabe que por ejemplo la celda J12 se modificó, pero no si es porque ya has escogido un elemento o puesto un filtro para el desplegable.

Así que por ahora cada vez que se escribe algo en J12, J13... escribimos en el log. Vamos con la parte contraria. Cada vez que vamos a borrar tenemos que ver en el log, en que fila es la última que se escribió previamente en el formulario. La propia función además de borrar del log la información nos devuelve esa fila. Por tanto a la hora de borrar tendré en cuenta esta fila. Una forma es pasarle el valor a CalculaRangoOrigen en la forma:

Call BorraFiltro(CalculaRangoOrigen (Range("T" & BorraLog)))

En este ejemplo se borrarían de la columna A, las filas indicadas desde la última que hay incluida en el log hasta el fina. Si por ejemplo lo último que escribimos en el log tiene su origen en la selección hecha en la celda J13, donde teníamos "MC DOMI", con lo anterior se nos borraría el rango T13:T16 porque empezando en la fila 13, el filtro hizo que se escribieran 4 filas en la columna T.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas