![]() |
|
#1
|
|||
|
|||
|
Hi,
for a report that has to get updates on a montly basis, I try to write a macro that would update all charts and tables in it automatically. My approach is to bookmark every table and every chart and to export the list of bookmarks to Excel (macro for this works well). I then add, for each bookmark, the path to the file where the relevant table/chart is located, the corresponding worksheet as well as the chart name (or table range). Let's only discuss the charts here. The macro I am currently working on would read this reference file, grab the charts in the right file and replace them at the bookmark location in word. I am at a point where it works, here is the code: Code:
Sub UpdateWordFromExcelMappings()
Dim xlApp As Object
Dim xlRefWB As Object
Dim xlDataWB As Object
Dim xlSheet As Object
Dim chartObj As Object
Dim chartImg As Object
Dim i As Long, lastRow As Long
Dim wordBookmark As String
Dim filePath As String, sheetName As String, rangeOrChart As String
Dim bmRange As Range
' Reference file and worksheet names
Const REF_FILE_PATH As String = "\\Path\to\the\reference\file.xlsx"
Const TABLE_SHEET As String = "Tables"
Const CHART_SHEET As String = "Graphs"
' Start Excel
Set xlApp = CreateObject("Excel.Application")
Set xlRefWB = xlApp.Workbooks.Open(REF_FILE_PATH, False, True)
' === Handle Charts ===
With xlRefWB.Sheets(CHART_SHEET)
lastRow = .Cells(.Rows.Count, 1).End(xlUp).row ' Find last used row in column A
For i = 2 To lastRow
' Read and trim values from reference file
wordBookmark = Trim(CStr(.Cells(i, 1).Text))
filePath = Trim(CStr(.Cells(i, 5).Text))
sheetName = Trim(CStr(.Cells(i, 6).Text))
rangeOrChart = Trim(CStr(.Cells(i, 7).Text))
' Skip row if required info is missing
If wordBookmark = "" Or filePath = "" Or sheetName = "" Or rangeOrChart = "" Then
Debug.Print "Skipping chart row " & i & " due to missing data"
Else
' Open chart source workbook (or reuse the reference file if same)
If xlRefWB.FullName = filePath Then
Set xlDataWB = xlRefWB
Else
If Not WorkbookIsOpen(filePath) Then
Set xlDataWB = xlApp.Workbooks.Open(filePath, False, True)
Else
Set xlDataWB = xlApp.Workbooks(filePath)
End If
End If
' Try to get the worksheet
Set xlSheet = xlDataWB.Sheets(sheetName)
' Try to get the chart object
Set chartObj = Nothing
On Error Resume Next
Set chartObj = xlSheet.ChartObjects(rangeOrChart)
On Error GoTo 0
If Not chartObj Is Nothing Then
' Copy the chart as an image
chartObj.CopyPicture Appearance:=1, Format:=2 ' xlScreen, xlPicture
' Paste into Word if bookmark exists
If ActiveDocument.Bookmarks.Exists(wordBookmark) Then
Set bmRange = ActiveDocument.Bookmarks(wordBookmark).Range
bmRange.Paste
ActiveDocument.Bookmarks.Add wordBookmark, bmRange ' Re-add the bookmark after paste
End If
Else
MsgBox "Chart '" & rangeOrChart & "' not found in sheet '" & sheetName & "'", vbExclamation
End If
' Only close the workbook if it's not the reference file
If Not xlDataWB Is xlRefWB Then xlDataWB.Close False
End If
Next i
End With
' Cleanup
xlRefWB.Close False
xlApp.Quit
Set xlApp = Nothing
MsgBox "Tables and charts have been updated.", vbInformation
End Sub
Function WorkbookIsOpen(filePath As String) As Boolean
Dim wb As Workbook
On Error Resume Next ' In case of error (e.g., workbook not found)
Set wb = xlApp.Workbooks(filePath) ' Try to get the workbook
On Error GoTo 0 ' Reset error handling
If Not wb Is Nothing Then
WorkbookIsOpen = True ' Workbook is open
Else
WorkbookIsOpen = False ' Workbook is not open
End If
End Function
Code:
chartObj.Copy Code:
chartObj.CopyPicture Any idea how to solve this? Thank you! (code parts about tables removed here, that's another topic...) |
|
#2
|
|||
|
|||
|
The first mistake you have made is using Late Binding.
There is no value to using late binding in this instance. The only advantage to late binding is version independence, i.e. you can write code in a newer version and it will theoretically work in an older one. But that advantage is lost unless you also use late binding for the host application code as Office is usually installed as a package. It is better to use early binding when working with other Office applications and reserve late binding for other commonly used libraries, e.g. ADO or XML. By adding a reference to the Excel library (Tools | References) and declaring the object variables with their correct types (eg. Dim chartObj As xl.Chart) you get the benefit of IntelliSense. Instead of Code:
chartObj.Copy Code:
chartObj.ChartArea.Copy Code:
bmRange.InlineShapes(1).Chart.ChartData.Activate bmRange.InlineShapes(1).Chart.ChartData.BreakLink |
|
#3
|
|||
|
|||
|
Thank you for the quick answer! Using early binding is for sure better, and I even already had the library references checked. I now modified the variable declarations:
Code:
Dim xlApp As Excel.Application
Dim xlRefWB As Excel.Workbook
Dim xlDataWB As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Dim chartObj As Excel.chartObject
Dim i As Long, lastRow As Long
Dim wordBookmark As String
Dim filePath As String, sheetName As String, rangeOrChart As String
Dim bmRange As Word.Range
Code:
chartObj.ChartArea.Copy ![]() I'll leave the screen a bit and see if I can find a solution later... |
|
#4
|
|||
|
|||
|
There is a compile error in the code you have posted for Function WorkbookIsOpen.
Code:
Set wb = xlApp.Workbooks(filePath) ' Try to get the workbook In your new variable declarations Code:
Dim chartObj As Excel.chartObject Code:
Dim chartObj As Excel.Chart |
|
#5
|
||||
|
||||
|
Why aren't you simply using a template with Links to the Excel workbook & charts, which you could simply update and, if appropriate, break before saving the new document? That would make most of your code redundant and wouldn't require the use of bookmarks.
__________________
Cheers, Paul Edstein [Fmr MS MVP - Word] |
|
#6
|
|||
|
|||
|
Quote:
That's true, though i didn't run into a compile error when executing my code But you're right, it's cleaner like this:Code:
Function WorkbookIsOpen(filePath As String, xlApp As Excel.Application) As Boolean Code:
If Not WorkbookIsOpen(filePath, xlApp) Then ... Quote:
I tried this, but then in this block it somehow jumps to the "else" condition and the message box is displayed Code:
If Not chartObj Is Nothing Then
' Copy the chart as an image
'chartObj.CopyPicture Appearance:=1, Format:=2 ' xlScreen, xlPicture
chartObj.ChartArea.Copy
'chartObj.Copy
' Paste into Word if bookmark exists
If ActiveDocument.Bookmarks.Exists(wordBookmark) Then
Set bmRange = ActiveDocument.Bookmarks(wordBookmark).Range
bmRange.Paste
ActiveDocument.Bookmarks.Add wordBookmark, bmRange ' Re-add the bookmark after paste
End If
Else
MsgBox "Chart '" & rangeOrChart & "' not found in sheet '" & sheetName & "'", vbExclamation
End If
Code:
Dim chartObj As Excel.Chart Code:
Dim chartObj As Excel.chart Quote:
I think it will be more easy to convince them to keep the reference file up to date than changing their file-naming habits...(I now also added "Option Explicit" at the beginning of my code to avoid getting into troubles concerning variable declaration) |
|
#7
|
||||
|
||||
|
Quote:
__________________
Cheers, Paul Edstein [Fmr MS MVP - Word] |
|
#8
|
|||
|
|||
|
It happens and is of no consequence. What is essential is that the object/datatype is valid. You can use IntelliSense or the Object Browser for that.
|
|
#9
|
|||
|
|||
|
Quote:
Thanks for the input, macropod, that's for sure something I will think about! You say: "all you need is a dialog box for selecting the source file" - that's actually the key point: Around 50 graphs in the document, around 10 people working on it, so there are plenty of source files (that's why I try to gather them in a reference file)...For the moment, I could achieve what I wanted to do with graphs. As often, a small mistake was hindering me: I noticed the graphs I am working with are not of type chartObject or Chart, but they are actually shapes! Therefore, the code in my previous post always jumped to the "else" condition. I replaced this code block and now loop through the shapes: Code:
Dim shp As Excel.Shape
For Each shp In xlSheet.Shapes
If shp.Type = msoChart And shp.Name = rangeOrChart Then
shp.Copy
If doc.Bookmarks.Exists(wordBookmark) Then
Set bmRange = doc.Bookmarks(wordBookmark).Range
bmRange.Paste
doc.Bookmarks.Add wordBookmark, bmRange
End If
chartFound = True
Exit For
End If
Next shp
Thanks again to both of you for your valuable guidance! Now, let's go for the tables in the document
|
|
#10
|
||||
|
||||
|
Quote:
What is more, with a little discipline, you could do all this with no VBA code at all! All that would require is that the source files always use the same worksheet & workbook names and are stored in the same folder.* If the ranges in those workbooks expand/contract, simply used named ranges and link to those. That way, whenever you create a new document based on the Word template, it can automatically update the links to the data. You can then break the links (for which you might use a trivial macro that executes before the document is first saved). *If necessary because the individual monthly files only contain the data for that month, this might be done by making copies of the source files once they've been prepared, renaming the copies with the standard names, then copying them to the required folder.
__________________
Cheers, Paul Edstein [Fmr MS MVP - Word] |
|
| Tags |
| charts, copy, update |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
Is there a way to stop macro modifications from undoing when Updating Word Linked Tables?
|
soroush.kalantari | Word VBA | 3 | 06-27-2021 06:15 PM |
| Problem With Updating Linked Excel Tables in Word 2013 | maverick1714 | Word | 6 | 09-30-2014 12:30 AM |
Word 2013 Fields not updating
|
awaywithpixie | Word | 1 | 09-10-2013 10:35 AM |
| Updating lists of figures/Tables in a protected document | IHARKIS | Word | 0 | 03-02-2011 04:39 AM |
| Auto-updating basic math functions & cross-referencing tables | FranklinBluth | Word Tables | 13 | 11-19-2009 10:26 AM |