domingo, enero 27, 2008

Programación de eventos en Excel – Segunda Nota

En la primera nota sobre programación de eventos en Excel vimos la importancia y el poder de esta herramienta. En esta nota daremos algunos ejemplos, poniendo el énfasis al aspecto práctico. Pero empecemos por señalar algunas características importantes de los eventos.
Existe una jerarquía de eventos:


  • Eventos de la aplicación (Application events)


  • Eventos del cuaderno (Workbook events)


  • Eventos de la hoja (Worksheets events)


  • En el tope de la jerarquía están los eventos de la aplicación, luego los del cuaderno y finalmente los de la hoja. Cada objeto contiene sus propios eventos y los de de los objetos que se encuentran por debajo de él en la jerarquía. Por ejemplo el cuaderno tiene un evento Worksheet_Change que responde a cada cambio en una celda de la hoja. El cuaderno, a su vez, tiene un evento Workbook_SheetChange que responde a un cambio en una celda de cualquiera de las hojas del cuaderno. Ante un cambio en una celda, los eventos de los tres niveles entran en acción.

    Ciertas acciones disparan más un evento, pero en cierto orden preestablecido. Por ejemplo, agregar una nueva hoja a un cuaderno (la acción) dispara una serie de eventos exactamente en este orden:

  • Workbook_SheetDeactivate

  • Workbook_SheetActivate

  • Workbook_NewSheet


  • Un tercer punto, y el más importante del punto de vista práctico, es cómo evitar generar "loops" infinitos al programar eventos. Por ejemplo, consideremos este código

    Private Sub Worksheet_Change(ByVal Target As Range)
    Target.Value = Target.Value + 1
    End Sub

    Ante un cambio en la hoja, el valor de la celda activa (Target) aumenta en 1. Esta acción en sí misma constituye un cambio, lo que produce un evento Change que vuelva a agregar 1 al valor de la celda. Y así sucesivamente hasta el infinito (o hasta que pulsemos Ctrl+Break).
    Si queremos ejecutar este código sin entrar en un "loop" infinito, debemos usar la propiedad EnableEvents de la Aplicación. En nuestro ejemplo:

    Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
    Target.Value = Target.Value + 1
    Application.EnableEvents = True
    End Sub

    Primero llevamos el valor de la propiedad a False, ejecutamos la (o las) línea y volvemos a poner el valor de EnableEvents a True. Esto es importante, ya que el valor de la propiedad no vuelve al valor de defecto (True) una vez concluido el código.

    Ya hemos visto algunos ejemplos de eventos en la nota anterior y también en la nota sobre el numerador automático de facturas.

    Ejemplos de eventos de cuaderno (Workbook events):

    Seleccionar la celda A1 al activar una hoja

    Private Sub Workbook_SheetActivate(ByVal Sh As Object)
       [A1].Select
    End Sub

    Dado que usamos un evento a nivel de cuaderno, al seleccionar cualquier hoja, la celda A1 será la celda activa (excepto que seleccionemos una hoja de gráfico, en cuyo caso ocurrirá un error).

    Al agregar una nueva hoja al cuaderno, le fecha y hora aparecen en la celda A1

    Private Sub Workbook_NewSheet(ByVal Sh As Object)

    If TypeName(Sh) = "Worksheet" Then
       [A1] = "La hoja fue agregada el " & Now
    End If

    End Sub

    Ejemplos de eventos de hoja

    En la nota anterior mostramos un ejemplo de Worksheet_Change. Otro ejemplo interesante de este evento es el siguiente que pone un fondo de color a la fila y la columna de la celda activa

    Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Cells.Interior.ColorIndex = xlNone
     With Target
       .EntireRow.Interior.ColorIndex = 35
       .EntireColumn.Interior.ColorIndex = 35
     End With
    End Sub

    Otros ejemplos pueden encontrarse en notas anteriores en este blog, como Limitar el área de trabajo en Excel o en cómo crear un menú en Excel.



    Technorati Tags:

    9 comentarios:

    1. Hola Jorge,

      Quiero hacerte una consulta. ¿Qué evento sería necesario para actualizar automáticamente la fecha del sistema sólo en una hoja?
      Tengo varias hojas donde quiero dejar registrada la fecha de la modificación en una celda concreta de cada hoja, por ejemplo A1, sólo de esa hoja y únicamente si ha habido algún cambio.
      Lo que tengo es esto:

      Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
      Range("A1").Select
      ActiveCell.FormulaR1C1 = "=Today()"
      Selection.NumberFormat = "dd/mmm/yyyy"
      Selection.Copy
      Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
      :=False, Transpose:=False
      Application.CutCopyMode = False
      End Sub

      Sé que no es el evento Workbook_BeforeSave el que tengo que utilizar pero con Workbook_SheetChange no lo he conseguido.

      Gracias por tu ayuda y como siempre, por todo el Blog.

      Un saludo,
      Sergio

      ResponderBorrar
    2. Sergio,
      la explicación requiere más espacio del que es razonable para un comentario.
      Estaré publicando una nota sobre el tema en lls próximos días.

      ResponderBorrar
    3. Jorge, tengo el siguiente procedimiento:

      Sub Aviso()
      Range("E10").Select
      While ActiveCell.Value <> Empty
      If IsNumeric(ActiveCell.Value) Then
      If ActiveCell.Value > 2.8 Then MsgBox "Celulas muertas,
      Aseo, Vuelta Vieja, Falta de Nutrientes, Prefil de Temperatura"
      End If
      ActiveCell.Offset(1, 0).Select
      Wend
      End Sub

      El cual hace que cada vez que un parametro anotado en la columna E se salga de cierto rango me tire un aviso de los posibles por qué de esa falla, ahora mi duda es: ¿Cómo puedo hacer para que estos avisos salgan cada vez que ingreso el valor en cada celda, una opción es que se ejecute cada vez que apriete "enter"?

      Me sería de mucha ayuda solucionar este problema.

      Gracias

      ATTE

      Álvaro Aguayo Almendra

      ResponderBorrar
    4. Álvaro,
      no me queda clara tu consulta. EN primer lugar, para que la rutina trabaje como evento tenés que ponerla en un módulo de la hoja correspondiente y tenés que usar algunos de los eventos de la hoja, En tu caso el apropiado sería el evento Change.
      Te sugiero que sigamos la consulta por mail privado. Por favor, lee lo que pongo en el enlace Ayuda (en la parte superior del blog).

      ResponderBorrar
    5. Espectacular tu ayuda, me sirvió mucho con la programación luego de un refresh de un data table, ya que no encontré el evento en vba, así que apliqué el enable events. Gracias Master.

      ResponderBorrar
    6. Hola Jorge que evento tengo hacer y como para que no me marque mensaje de error cuando quiero hacer una consulta externa a una pagina y esta no esta disponible, ya que al enviarme msg de error se queda en espera de un aceptar y esto me impide que cuando ya puede conectarse a la pagina lo pueda hacer, ya que primero tengo que quitar el msg de error dando aceptar. El detalle que el archivo que hace esto esta en un servidor y va y toma datos de una pagina y los va guardando en un historia. me podras ayudar con esto.
      en espera de tu respuesta, Rossy Vargas

      ResponderBorrar
    7. Tienes que usar la propiedad Application.DisplayAlerts=False.
      Y por supuesto, volver a establecer el valor de la propiedad a True al finalizar la rutina.

      ResponderBorrar
    8. Hola Jorge.. no me funciono la propiedad Application.DisplayAlerts=False ya que Excel me sigue enviando msg cuando no puede refrescar una hoja con un vinculo a una pagina web. como te puedo mandar mas detalles de las pantallas para que me ayudes ?? he buscado mucho en internet y no encuentro una solución.

      ResponderBorrar
    9. Fijate en el enlace "Ayuda" en la parte superior del blog.

      ResponderBorrar

    Nota: sólo los miembros de este blog pueden publicar comentarios.