domingo, 5 de julio de 2020

Interactuando con Microsoft Word (IV): Enviar datos de celdas de Excel hacia objetos de un archivo Word

¡Hola nuevamente! En esta ocasión seguiremos interactuando con Word y lo que toca hacer es enviar, nuevamente, datos, pero ya no una tabla completa sino, datos específicos a cajas de texto en el archivo de Word, como si fuera un reporte y Excel nuestra base de datos. Para mí es más práctico usar la herramienta “Combinar correspondencia” desde el mismo Word, pero sé que para muchos es mejor hacer todo desde Excel, así que, manos a la obra.

Lo primero es tener datos, por ejemplo, del siguiente modo:

Ojo que en la celda B1 hay una lista desplegable de los datos de la columna “Id” de la Tabla (Asumo que saben cómo hacerla).

En Word tendremos un documento así:

Por si no lo saben, aquellas partes del Word en donde dice “Haga clic o pulse aquí para escribir texto”, cada uno es un “Control de contenido de texto sin formato” que serán los campos en donde insertemos el texto que tenemos en Excel. Para usarlos, activen la pestaña “Programador” de su Word y en el grupo “Controles” los encontrarán.

Para que sea fácil su identificación en el archivo Word vamos a modificar la propiedad “Título” de cada control, no será difícil si vamos seleccionado cada uno y presionamos el botón “Propiedades” del mismo grupo “Controles” y agregamos el título que elijamos.

En mi caso elegí TxtId, TxtNombres, TxtSexo, etc.

Ahora sugiero releer un par de artículos sobre las referencias a activar y el objeto Word (Enlace1) y otro sobre cómo detectar archivos abiertos (Enlace2). Del segundo enlace copiaremos en nuestro archivo Excel la UDF “IsFileOpen”.

Luego vamos a declarar las variables y abrir el archivo Word al que enviaremos los datos. Ya que enviaremos solo un registro a la vez, es importante que previamente se haya elegido un dato en la lista desplegable de la celda B1..

 If Range("B1") = "" Then
     MsgBox "Elija un dato de la lista", vbOKOnly, "Todo Sobre Excel"
     Exit Sub
 End If 

strArchivo = ThisWorkbook.Path & "\Formulario de datos.docx" 

If Dir(strArchivo) = "" Then
      MsgBox "No existe el archivo", vbOKOnly, "Todo Sobre Excel"
      Exit Sub 
End If 

If IsFileOpen(strArchivo) Then
      'si está abierto el archivo word
      Set objDoc = GetObject(strArchivo)
      Set MiappWord = objDoc.Application
 Else
      'si está cerrado el archivo Word
      Set MiappWord = CreateObject("Word.Application")
      MiappWord.Documents.Open strArchivo
      MiappWord.Visible = True 
End If 

Algo que no hicimos al inicio es declarar las variables que usaremos para los datos que enviaremos a Word. Asimismo, como en Excel nuestros datos están como Tabla, declararemos una variable para dicha tabla. En un momento veremos para qué es todo eso

Dim strNombre$, strSexo$, strDireccion$, strPrograma$ 
Dim FechaN as Date 
Dim MiTabla as ListObjects 

Para hallar los valores que corresponda al ID que elijamos en la celda B1, vamos a usar la WorkSheetFunction llamada VlookUp, de funcionamiento similar a BuscarV. Yo prefiero usar Find, pero para el ejercicio considero que será más fácil entender la función elegida. Esos sí, como buscaremos los datos en una Tabla, primero asignaremos el objeto Tabla a la variable respectiva y ya después usaremos el VLookUp.

Set miTabla = Hoja1.ListObjects("Tabla1")

With Application.WorksheetFunction
     strnombre = .VLookup(Range("B1"), miTabla.Range, 2, 0)
     strsexo = .VLookup(Range("B1"), miTabla.Range, 3, 0)
     fechan = .VLookup(Range("B1"), miTabla.Range, 4, 0)
     strdireccion = .VLookup(Range("B1"), miTabla.Range, 5, 0)
     strprograma = .VLookup(Range("B1"), miTabla.Range, 6, 0) 
End With 

Como ya tenemos los valores en nuestras variables, ahora sí comenzaremos a trabajar con el objeto Word y con los controles que ahí tenemos. Como vamos a trabajar con el documento activo, y hemos colocado nuestros propios títulos a cada control de nuestro archivo Word, usaremos también el método SelectContentControlsByTitle así como el objeto Range del control, que representa su área continua. Por último, a través de la propiedad Text le daremos el valor de nuestras variables de Excel. Ah, ojo con los nombres de los títulos de los controles ya que son sensibles a mayúsculas y minúsculas. El resultado en Word debe verse así:

Juntando todo y ordenando, deberíamos tener esto:

Option Explicit
 'Todo Sobre Excel
 'Abraham Valencia
 'https://abrahamexcel.blogspot.com/
 'https://www.facebook.com/TodosobreExcelAV/
 'https://twitter.com/Todosobre_Excel
 'Lima, Perú
 'Julio del 2020

 Sub EnviarDatosaWord() 

Dim MiappWord As Word.Application 
Dim objDoc As Object 
Dim strArchivo$, strnombre$, strsexo$, strdireccion$, strprograma$ 
Dim fechan As Date 
Dim miTabla As ListObject 

If Range("B1") = "" Then
     MsgBox "Elija un dato de la lista", vbOKOnly, "Todo Sobre Excel"
     Exit Sub 
End 

If strArchivo = ThisWorkbook.Path & "\Formulario de datos.docx" If Dir(strArchivo) = "" Then
      MsgBox "No existe el archivo", vbOKOnly, "Todo Sobre Excel"
      Exit Sub 
End If 

Set miTabla = Hoja1.ListObjects("Tabla1") 

With Application.WorksheetFunction
     strnombre = .VLookup(Range("B1"), miTabla.Range, 2, 0)
     strsexo = .VLookup(Range("B1"), miTabla.Range, 3, 0)
     fechan = .VLookup(Range("B1"), miTabla.Range, 4, 0)
     strdireccion = .VLookup(Range("B1"), miTabla.Range, 5, 0)
     strprograma = .VLookup(Range("B1"), miTabla.Range, 6, 0) 
End With 

If IsFileOpen(strArchivo) Then
      'si está abierto el archivo word
      Set objDoc = GetObject(strArchivo)
      Set MiappWord = objDoc.Application 
Else
      'si está cerrado el archivo Word
      Set MiappWord = CreateObject("Word.Application")
      MiappWord.Documents.Open strArchivo
      MiappWord.Visible = True 
End If 

With MiappWord.ActiveDocument
     .SelectContentControlsByTitle("TxtId").Item(1).Range.Text = Hoja1.Range("B1").Value
     .SelectContentControlsByTitle("TxtNombres").Item(1).Range.Text = strnombre
     .SelectContentControlsByTitle("TxtSexo").Item(1).Range.Text = strsexo
     .SelectContentControlsByTitle("TxtFechaN").Item(1).Range.Text = fechan
     .SelectContentControlsByTitle("TxtDirección").Item(1).Range.Text = strdireccion
     .SelectContentControlsByTitle("TxtPrograma").Item(1).Range.Text = strprograma
     .SelectContentControlsByTitle("TxtFechaI").Item(1).Range.Text = Date     
 End With

 'Dejamos el cursor al final de la página 
MiappWord.Selection.EndKey Unit:=wdStory 

MsgBox "Todo listo", vbOKOnly, "Todo Sobre Excel" 

'Esta línea no es necesaria, pero por costumbre algunos la usamos 
Set MiappWord = Nothing 

End Sub 

Y listo, eso es todo por hoy. Ah, por supuesto que si quieren pueden mejorar el formato del Word, al ser este un ejemplo pues no le dedico tiempo a ese tipo de detalles ¡Hasta la próxima!

Abraham Valencia
Lima, Perú

Descargue el ejemplo de aquí

jueves, 25 de junio de 2020

Interactuando con Microsoft Word (III): Abrir archivos de Word, o detectar si ya están abiertos, para trabajar con ellos desde Excel usando VBA.

En los artículos anteriores de cómo interactuar con Microsoft Word (Enlace1, Enlace2), vimos básicamente la forma de enviar datos desde una hoja de Excel a un archivo de Word nuevo, pero ¿Qué pasa si queremos enviar nuestros datos a un archivo que ya existe? La primera respuesta que quizá se les viene a la mente es “pues lo abrimos ¡obvio!”, claro, eso es lo que haremos, aunque iremos un poquito más allá y veremos que hacer si ya está abierto el archivo en cuestión.

Ya saben ustedes que lo primero es activar las referencias respectivas a Microsoft Word en el editor de VBA, después de eso vamos a declarar las variables que vamos a usar. Una para crear un objeto que nos ayudará a trabajar con el archivo Word si ya está abierto, una para crear el objeto Word y otra para la ruta del archivo.

Dim objDoc As Object 
Dim MiappWord As Word.Application 
Dim strArchivo$ 

Ahora vamos a darle valor a la variable con la ruta del archivo, asumiendo que el archivo Word y el de Excel están en la misma carpeta.

strArchivo = ThisWorkbook.Path & "\ArchivoEjemploWord.docx " 

Como para que no quede duda de que todo saldrá bien, usando Dir nos aseguramos que el archivo exista.

If Dir(strArchivo) = "" Then
     MsgBox "No existe el archivo", vbOKOnly, "Todo Sobre Excel"
     Exit Sub
 End If 

De no existir el archivo, un MsgBox nos lo indicará y se terminará el proceso.

Ahora algo importante, para saber si el archivo está abierto vamos a usar una UDF que Microsoft puso a disposición pública hace año (ahora la página no existe), se puede mejorar, claro, pero dado que hay algo ya hecho ahorremos tiempo. Pueden ponerla en el mismo módulo de la rutina que estamos haciendo o en otro, no hay problema.

' This function checks to see if a file is open or not. If the file is
' already open, it returns True. If the file is not open, it returns 
' False. Otherwise, a run-time error occurs because there is 
' some other problem accessing the file. 
' Código de macro para comprobar si un archivo ya está abierto 
' http://support.microsoft.com/kb/291295/es 

Function IsFileOpen(filename As String) 
Dim filenum As Integer, errnum As Integer 
' 
On Error Resume Next   ' Turn error checking off. 
filenum = FreeFile()   ' Get a free file number. 
' Attempt to open the file and lock it. 
Open filename For Input Lock Read As #filenum 
Close filenum          ' Close the file. 
errnum = Err           ' Save the error number that occurred. 
On Error GoTo 0        ' Turn error checking back on. 
' Check to see which error occurred. 
Select Case errnum     
' No error occurred.     
' File is NOT already open by another user. 
Case 0
     IsFileOpen = False
     ' Error number for "Permission Denied."
     ' File is already opened by another user. 
Case 70
     IsFileOpen = True
     ' Another error occurred.
 Case Else
     Error errnum 
End Select 
End Function 

Entonces, ya con nuestra UDF y un If, vamos a detectar si el archivo está abierto, y de estarlo, con la función GetObject vamos a hacer la referencia respectiva al archivo (objDoc) y como lo que haremos es crear un objeto, vamos luego a convertirlo en nuestro objeto Word (MiappWord). De estar cerrado (Else), crearemos un objeto Word, abriremos el documento del ejemplo y lo haremos visible. Miren y analicen el código.

If IsFileOpen(strArchivo) Then
     'si está abierto
     Set objDoc = GetObject(strArchivo)
     Set MiappWord = objDoc.Application 
Else
     'si está cerrado
     Set MiappWord = CreateObject("Word.Application")
     MiappWord.Documents.Open strArchivo
     MiappWord.Visible = True 
End If 

Sea abierto o cerrado, una vez que tengamos el control del archivo, y para que se vea como se puede hacer cosas en él, dejaré una línea simple que lo único que hará es llevar el cursor al final del archivo Word.

MiappWord.Selection.EndKey Unit:=wdStory

Nuestra rutina completa debe de quedar así:

Option Explicit
'Todo Sobre Excel 
'Abraham Valencia 
'https://abrahamexcel.blogspot.com/ 
'https://www.facebook.com/TodosobreExcelAV/ 
'https://twitter.com/Todosobre_Excel 
'Lima, Perú 
'Junio del 2020 

Sub AbrirWord() 

Dim MiappWord As Word.Application 
Dim objDoc As Object 
Dim strArchivo$ 

strArchivo = ThisWorkbook.Path & "\ArchivoEjemploWord.docx " 

If Dir(strArchivo) = "" Then
     MsgBox "No existe el archivo", vbOKOnly, "Todo Sobre Excel"
     Exit Sub 
End If 

If IsFileOpen(strArchivo) Then
     'si está abierto
     Set objDoc = GetObject(strArchivo)
     Set MiappWord = objDoc.Application 
Else
     'si está cerrado
     Set MiappWord = CreateObject("Word.Application")
     MiappWord.Documents.Open strArchivo
     MiappWord.Visible = True
 End If 

MiappWord.Selection.EndKey Unit:=wdStory 

End Sub 

Y listo, eso es todo en esta ocasión y la prometo que continuaremos con más sobre interactuar con Word desde Excel a través de VBA ¡Hasta la próxima!

Abraham Valencia
Lima, Perú

Descargue el ejemplo de aquí

domingo, 21 de junio de 2020

Interactuando con Microsoft Word (II): Enviar datos de Excel a Word (VBA) y darles formato.

Hace varias semanas le comenté sobre cómo comenzar a interactuar entre Excel y Word usando VBA. (Enlace). En aquella ocasión sobre todo nos centramos en enviar tablas hacia Word en diversos formatos. En esta ocasión iremos un poco más allá y enviaremos datos y modificaremos su formato en el archivo de Word creado, entonces ¡Manos a la obra!

Para el ejemplo vamos a usar una cosa muy simple de datos en Excel, así:

Ahora, lo primero, como ya saben, será activar las referencias a “Microsoft Word 16.0 Object Library”, hecho eso corresponde declarar nuestras variables, en donde la primera será la de nuestro objeto Word.

Dim MiappWord As Word.Application

Ahora vamos a crear el objeto Word y un documento nuevo en blanco.

Set MiappWord = CreateObject("Word.Application") 
MiappWord.Documents.Add 
Como ya está creado nuestro objeto y ya tenemos un archivo nuevo en él, así no lo veamos, copiaremos nuestros datos de Excel.
Hoja1.Range("A1:C11").Copy

A partir de aquí empezaremos a usar el objeto Word por lo que usaremos la instrucción With para ahorrarnos algunas líneas de código.

With MiappWord 

Luego pegaremos nuestros datos en Word pero manteniendo el formato de origen.

  .Selection.PasteExcelTable False, False, False

Como es un archivo nuevo, la tabla pegada tendrá el índice 1, por lo que para referirnos a ella usaremos dicho índice como tabla dentro del documento activo (ActiveDocument) de Word y por ende, para seguir facilitándonos escribir un poco menos, usaremos otro With, ya que los formatos a aplicar estarán dentro de celdas de dicha tabla.

With .ActiveDocument.Tables(1)

Como en la tabla tenemos objetos Cell, lo que haremos es cambiar las propiedades respectivas de cada uno de los objetos que requerimos. Voy a dejar las líneas en VBA comentadas para que se sepa qué hace cada una.

         'Fuente de color rojo
         .Cell(2, 3).Range.Font.ColorIndex = wdRed
         'Fondo de color verde obtenido con constante de Word
         .Cell(3, 3).Shading.BackgroundPatternColor = wdColorGreen
         'Fondo de color verde gris obtenido con RGB
         .Cell(4, 3).Shading.BackgroundPatternColor = RGB(184, 183, 153)
         'Fuente en negrita
         .Cell(5, 3).Range.Font.Bold = wdToggle
         'Fuente en cursiva
         .Cell(6, 3).Range.Font.Italic = wdToggle
         'Fondo en rojo para toda una fila
         .Rows(7).Shading.BackgroundPatternColor = wdColorRed
         'Escribir hola en una celda
         .Cell(Row:=8, Column:=3).Range = "Hola"
 End With 

Para seguir aprendiendo sobre este tema, también vamos a ver cómo hacer cambios a varias celdas de la tabla que son contiguas, pero están en distintas filas y columnas.  Lo primero es definir dicho rango de celdas. En este caso iniciaremos en la fila 9, columna 1 y terminaremos en la fila 11, columna 3.

With .ActiveDocument
    Set mirango = .Range(Start:=.Tables(1).Cell(9, 1).Range.Start, End:=.Tables(1).Cell(11, 3).Range.End)
End With 

Para que no se mareen con los With, tranquilos/as, al final veremos todo junto. Volviendo a lo anterior, ya definido el rango, a través de un For-Each vamos a colocar la misma palabra en dichas celdas.

For Each miCelda In mirango.Cells
     miCelda.Range = "Repetir"
Next miCelda  

Quizás si tenemos todo junto nos sea más fácil entenderlo:

Option Explicit
'Todo Sobre Excel 
'Abraham Valencia 
'https://abrahamexcel.blogspot.com/ 
'Lima, Perú 
'Junio del 2020 

EnviardatosaWord() 

Dim MiappWord As Word.Application 
Dim mirango As Word.Range, miCelda As Word.Cell 

Set MiappWord = CreateObject("Word.Application") 
MiappWord.Documents.Add Hoja1.Range("A1:C11").Copy 

With MiappWord

          .Selection.PasteExcelTable False, False, False

          With .ActiveDocument.Tables(1)
              'Fuente de color rojo
              .Cell(2, 3).Range.Font.ColorIndex = wdRed
	            'Fondo en verde con constante de Word         
              .Cell(3, 3).Shading.BackgroundPatternColor = wdColorGreen
              'Fondo en verde gris con función RGB         
              .Cell(4, 3).Shading.BackgroundPatternColor = RGB(184, 183, 153)
              'Fuente en formato negrita
              .Cell(5, 3).Range.Font.Bold = wdToggle
              'Fuente en formato cursiva
              .Cell(6, 3).Range.Font.Italic = wdToggle
              'Fondo de color rojo para toda una fila
              .Rows(7).Shading.BackgroundPatternColor = wdColorRed
              'Escibir hola en una celda
              .Cell(Row:=8, Column:=3).Range = "Hola"
           End With

           With .ActiveDocument
               Set mirango = .Range(Start:=.Tables(1).Cell(9, 1).Range.Start, End:=.Tables(1).Cell(11, 3).Range.End)
           End With

          .Visible = True 
End With 

'Escribir la palabra Repetir en todas las celdas del rango elegido
For Each miCelda In mirango.Cells
     miCelda.Range = "Repetir" 
Next miCelda 

Application.CutCopyMode = False 
Set MiappWord = Nothing 

End Sub  

Si todo salió bien, en su Word deben tener algo como esto:

Y eso es todo en esta ocasión, espero que hayan aprendido un poco más sobre interactuar con Word desde Excel. Hasta la próxima.

Abraham Valencia
Lima, Perú

Descargue el ejemplo de aquí

 

viernes, 12 de junio de 2020

Publicando en Facebook desde Excel con VBA (II)

Hace algunos días vimos como publicar un post de texto en nuestras páginas de Facebook (Enlace). Además, ahí explicamos cómo obtener la clave token de nuestra página, la cual es necesaria para poder seguir publicando diversas cosas como imágenes, que es lo que haremos en esta oportunidad.

Como asumo que ya todos/as leyeron el artículo anterior, ahora me centrare en las características que debe tener la imagen que vamos a usar. Ah, eso sí, la imagen debe estar ya en internet de manera previa, es decir, vamos a usar una imagen que hayamos colocado en alguna web o de alguna web que lo permita. Sobre el formato, pueden usarse los siguientes: JPG/JPEG, BMP, PNG, GIF y TIFF. “Facebook For Developers” recomienda que los archivos PNG tengan un tamaño menor a 1 MB, asimismo, para otros formatos recomiendan imágenes de menos de 4 MB.

Para lograr nuestro cometido, nuevamente usaremos el objeto MSXML2.XMLHTTP60 y el método POST. Para ello los parámetros que usaremos en esta ocasión serán los siguientes:

"https://graph.facebook.com/[Aquí_el_ID_de_la_página]/photos?&url=[Aquí_la_URL_de_tu_imagen]&caption=[Aquí_el_texto_que_queremos_con_la_foto]&access_token=[Aquí_tu_clave_token]"

Por si acaso, una forma fácil de conseguir el URL de una imagen es darle clic derecho y elegir la opción “Copiar dirección de imagen” o “Copiar vínculo de la imagen” (el mensaje exacto dependerá del navegador d que uses).

Ahora sí vamos a lo que les gusta, el VBA. Lo primero será declarar nuestras variables. No ovliden activar las referencias a “Microsoft XML, v6.0” (el 6 puede variar dependiendo de tu versión de Office).

Dim WebXML As New MSXML2.XMLHTTP60 
Dim miURL As String
Ahora a la variable miURL, le vamos a asignar la cadena dar con parámetros que necesitamos y habíamos comentado líneas arriba.  Como verán los parámetros deben ser reemplazados en la cadena (sin los corchetes, por si acaso.). Una vez conseguida la cadena, se la asignamos a la variable, quedando en mi caso así:
miURL = "https://graph.facebook.com/ 389999999905731/photos?&url=https://1.bp.blogspot.com/9TKACuCKzyA/XtmyFrZXGNI/AAAAAAAADzc/YofqAsYp1SHGi9U9reyxtitmzOIQCK4BGAsYHg/w400h330/Face11.jpg&caption=Imagen de prueba&access_token=EAAkUiClZCzDgBAB0ZA2P4iGZBdNjC1O999JvsI8AeZBufiwUw18jAAOZAF9nZB30QEIeEZCfXh6ZC1KsE9yNXad7rTnZCpOkRURpbbIt1SU35P2z4GXhBMNtdwZB8HggZBL1LZBwXAyeSpo844y1n4a3U8KcdZCHsjyPCAZAuSrHyLYjgZDZD"

Lo que toca ahora es usar la instrucción Open combinada con el método Post para configurar la solicitud (cadena URL) a la web y de inmediato usaremos el método Send para abrir la conexión y enviar la solicitud al servidor. Todo junto debe quedar así: 

Sub EnviaraFacebook()  

Dim WebXML As New MSXML2.XMLHTTP60  
Dim miURL As String 

miURL = "https://graph.facebook.com/ 389999999905731/photos?&url=https://1.bp.blogspot.com/9TKACuCKzyA/XtmyFrZXGNI/AAAAAAAADzc/YofqAsYp1SHGi9U9reyxtitmzOIQCK4BGAsYHg/w400h330/Face11.jpg&caption=Imagen de prueba&access_token=EAAkUiClZCzDgBAB0ZA2P4iGZBdNjC1O999JvsI8AeZBufiwUw18jAAOZAF9nZB30QEIeEZCfXh6ZC1KsE9yNXad7rTnZCpOkRURpbbIt1SU35P2z4GXhBMNtdwZB8HggZBL1LZBwXAyeSpo844y1n4a3U8KcdZCHsjyPCAZAuSrHyLYjgZDZD" 

WebXML.Open "POST", URL, False  
WebXML.send  

MsgBox "Listo"  

End Sub 

Al correr la macro, en nuestra página tendremos algo así:

¿Notan que, nuevamente, sale el nombre de la app creada para Facebook en el primer artículo? Ese post no lo borraré para que quede como una nueva prueba. Les recuerdo que el token les seguirá sirviendo para próximas publicaciones.

Eso es todo en esta oportunidad ¡Hasta la próxima!

Abraham Valencia
Lima, Perú