I want, in Word VBA, to repeat copying content from Excel to Word.
Goal: I have a range in an Excel workbook about 250 cells long in column C that is a list of figure titles. I want to paste those titles into Word, as ‘captions’ (while leaving space to put the figures later, putting a consistent source caption on them, etc.)
I wrote code for one cell. I want to loop down to the next cell and insert a new caption with that new title, until all 250 distinct titles are entered.
Here is the code. I have it running a function, which runs a sub to get the title from one cell.
Sub Macro123()
Selection.InsertCaption Label:="Figure", TitleAutoText:="InsertCaption2", _
Title:=".", Position:=wdCaptionPositionBelow, ExcludeLabel:=0
Selection.TypeText Text:=TitleDrop
Selection.Style = ActiveDocument.Styles("EcoCaption")
Selection.TypeParagraph
Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
Selection.TypeParagraph
Selection.TypeText Text:="Source: Current study, based off landings data from CDFW."
Selection.Style = ActiveDocument.Styles("EcoSource")
Selection.TypeParagraph
Selection.TypeParagraph
Selection.TypeParagraph
End Sub
-----------
Function TitleDrop()
GetExcelTitles
Selection.PasteAndFormat (wdFormatPlainText)
End Function
-----------------
Sub GetExcelTitles()
Dim ObjXL As Object, xlWkBk
Dim strTitleName As String
On Error Resume Next
Set ObjXL = GetObject(, "Excel.Application")
If Err.Number <> 0 Then
MsgBox "No Excel Files are open (Excel is not running)"
Exit Sub
End If
For Each xlWkBk In ObjXL.Workbooks
If xlWkBk.Name = "130611 Figure Lists.xlsx" Then
xlWkBk.Sheets("Figuresonly").Range("C6").Select
xlWkBk.Sheets("Figuresonly").Range("C6").Copy
Exit For
End If
Next
Set ObjXL = Nothing
End Sub
Try changing some of your code to be like the following and make GetExcelTitles call your Paste Sub, not the other way around.
Dim rng as Range
For Each xlWkBk In ObjXL.Workbooks
If xlWkBk.Name = "130611 Figure Lists.xlsx" Then
For each xlWkBk.Sheets("Figuresonly").Range("C1", "C250")
rng.Select
rng.Copy
Call TitleDrop
Next
End If
Next
Cheers, LC
Related
I already have a Macro in Excel that pulls through data from specific tables, rows and columns in a specified Word doc and returns it to cells in my Excel s/sheet. I need to make 2 alterations to the code but my knowledge is not advanced enough.
I need to run this code on multiple Word docs in a specified folder, whether it is .doc or a .docx
I need to establish why on some Word docs, the code fails to pull through the data from the Word doc and I get RUN TIME ERROR CODE '4605' 'The method or property is not available because no text is selected'. I tried putting, 'on error resume next', at the start of the module so it keeps on running to the end, in the hope that some text would get pulled through, but still none of the cells in my Excel s/sheet get populated.
Sub ImportFromWord()
On Error Resume Next
'Activate Word Object Library
Dim WordDoc As Word.Document
Set WordApp = CreateObject("word.application") ' Open Word session
WordApp.Visible = False 'keep word invisible
Set WordDoc = WordApp.Documents.Open("C:\Users\brendan.ramsey\OneDrive - Ofcom\Objectives\Brendan's Objectives 2022-23\Licence calls\test 2.docx") ' open Word file
'copy third row of first Word table
WordDoc.Tables(1).Cell(Row:=1, Column:=3).Range.Copy
'paste in Excel
Range("A3").PasteSpecial xlPasteValues
WordDoc.Tables(4).Cell(Row:=3, Column:=6).Range.Copy
Range("B3").PasteSpecial xlPasteValues
WordDoc.Tables(4).Cell(Row:=3, Column:=3).Range.Copy
Range("C3").PasteSpecial xlPasteValues
WordDoc.Tables(5).Cell(Row:=2, Column:=5).Range.Copy
Range("D3").PasteSpecial xlPasteValues
WordDoc.Tables(5).Cell(Row:=2, Column:=7).Range.Copy
Range("E3").PasteSpecial xlPasteValues
WordDoc.Tables(5).Cell(Row:=2, Column:=2).Range.Copy
Range("F3").PasteSpecial xlPasteValues
WordDoc.Close 'close Word doc
WordApp.Quit ' close Word
End Sub
Your code may behave better if you avoid all that copy/paste and transfer the cell contents directly:
Sub ImportFromWord()
Const FLDR_PATH As String = "C:\Temp\Docs\"
Dim WordDoc As Word.Document, WordApp As Word.Application
Dim rw As Range, f
Set rw = ActiveSheet.Rows(3) 'or some other sheet
f = Dir(FLDR_PATH & "*.doc*") 'check for document
Do While Len(f) > 0
If WordApp Is Nothing Then 'open word if not already open
Set WordApp = CreateObject("word.application")
WordApp.Visible = False
End If
With WordApp.Documents.Open(FLDR_PATH & f, ReadOnly:=True) ' open Word file
WordCellToExcel .Tables(1).Cell(Row:=1, Column:=3), rw.Cells(1)
WordCellToExcel .Tables(4).Cell(Row:=3, Column:=6), rw.Cells(2)
WordCellToExcel .Tables(4).Cell(Row:=3, Column:=3), rw.Cells(3)
'etc etc
.Close savechanges:=False
End With
Set rw = rw.Offset(1) 'next row down
f = Dir() 'next file, if any
Loop
If Not WordApp Is Nothing Then WordApp.Quit ' close Word if it was opened
End Sub
'transfer content from a cell in a Word Table to an Excel range
Sub WordCellToExcel(wdCell As Word.Cell, destCell As Range)
Dim v
v = wdCell.Range.Text
destCell.Value = Left(v, Len(v) - 2) 'remove "end of cell" marker
End Sub
RUN TIME ERROR CODE '4605' 'The method or property is not available because no text is selected'.
Runtime Code 4605 happens when Microsoft Word fails or crashes whilst it's running. It doesn't necessarily mean that the code was corrupt in some way, but just that it did not work during its run-time. This kind of error will appear as an annoying notification on your screen unless handled and corrected. Here are symptoms, causes and ways to troubleshoot the problem.
As the error message says there is no text selected. To find out what property or method gives the error message I'd recommend breaking the chain of calls in the single line of code by declaring each property or method call on a separate line, so you will know which call fails exactly.
I am trying to create a VBA script that copies a cell value when a form control checkbox in the corresponding row is "checked". There are around 116 rows, and I only want to copy the cell value for the checked rows.
For example, my checkboxes are in cells D6:D122. If rows D6, D8, and D10 are checked, I want to copy the values within cells C6, C8, and C10, alphabetize the results and paste them into a newly generated Word document when a command button is clicked. I have figured out how to generate a new word document, but I have trouble copying over the cell values and alphabetizing them.
This is my code as of now:
Sub CommandButton1_click()
Dim wdApp As Word.Application
Dim var as Variant
Set wdApp = New Word.Application
With wdApp
.Visible = True
.Activate
.Documents.Add
End With
End Sub
One of the simpler solutions:
Sub CommandButton1_click()
Dim cl As Range, txt As String
For Each cl In ThisWorkbook.Worksheets(1).Range("D6:D122")
If cl Then txt = txt & vbLf & cl.Offset(0, -1)
Next cl
If Len(txt) > 0 Then ' show Word if you have something to output
Dim wdApp As New Word.Application ' declare and create object at once
With wdApp
.Documents.Add ' the added document automatically becomes active
.Selection.TypeText Mid(txt, 2) 'remove extra (lead) vbLf and output text to Word
.ActiveDocument.Range.Sort
.Visible = True 'show Word after processing to improve performance
End With
Else
MsgBox "There is nothing to output", vbInformation + vbOKOnly
End If
End Sub
On the sheet named "Data" I have an Excel Table. This table has a variable number of rows, typically 20k to 30k. Column A is "JobNo"
On the sheet named "Main" I have cell where I show the "JobNo". That value starts as the first visible JobNo from the filtered table.
I have buttons for "Next Record". When I click this button and run it's associated VBA code, I need that code to move the "Data" sheet's cell pointer to the next visible (filtered) value in column A.
I've tried several samples of code found here to find the first visible cell, and to move to the next visible cell, but most of them relied on "Activecell". I need to move a "virtual" pointer to the next visible cell because that sheet, where the table is located is not visible and so the ActiveCell is not there.
This for example works to move the cell pointer to the next visible cell, but it only works if "Dat" sheet is selected:
Sub movetest()
Sheets("Data").Range("A1").EntireColumn.SpecialCells(xlCellTypeVisible).Find(What:="*", After:=ActiveCell).Activate
End Sub
What I need is something that can do what the above line does, but do it to a sheet that is not selected. Bonus to me if it was in structured table syntax.
I also tried to use some variant of this, which moves to the first visible cell, but only when the "Data" sheet is selected:
Range("Data[[#All],[PACEJob]]").SpecialCells(xlCellTypeVisible).Find _
(What:="*", After:=ActiveSheet.Range("Data[[#Headers],[PACEJob]]"), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
EDIT:
This does what I need for non filtered table. Just need to replicate this to do the same thing with a filtered table and only show visible.
(GLobal selectedRow)
selectedJobRow = selectedJobRow + 1
Sheets("Main").Range("O2").Value = Sheets("Data").Range("A" & selectedJobRow).Value
I gave up trying to work around the sheet not being active and Activecell. This seems to work, although it seems like there would be a more elegant way, No?
Sub movePointerDown()
Application.ScreenUpdating = False
Set wksToCheck = Sheets("Data")
Sheets("data").Select
Sheets("Data").Range("A1").EntireColumn.SpecialCells(xlCellTypeVisible).Find(What:="*", After:=ActiveCell).Activate
Sheets("Main").Range("O2").Value = ActiveCell.Value
Sheets("Main").Select
Application.ScreenUpdating = False
End Sub
And its companion:
Sub movePointerUp()
Application.ScreenUpdating = False
Set wksToCheck = Sheets("Data")
Sheets("data").Select
Sheets("Data").Range("A1").EntireColumn.SpecialCells(xlCellTypeVisible).Find(What:="*", After:=ActiveCell, Searchdirection:=xlPrevious).Activate
Sheets("Main").Range("O2").Value = ActiveCell.Value
Sheets("Main").Select
Application.ScreenUpdating = False
End Sub
Don't need to work with ActiveCell if you use Excel objects, there is plenty of information about the subject on the internet.
This proposed solution returns the next Cell record, and it's wrapped in a Function
to allow for flexibility. It uses a Static variable to keep track of actual record (see link provided for details) and validates the ListObject (excel Table) field, its AutoFilter and whether the actual record is the last visible record.
Function ListObject_ƒNextVisibleCell(rOutput As Range, sMsgOut As String, sFld As String) As Boolean
Static rCll As Range
Const kMsg1 As String = "Field [ #FLD ] not found."
Const kMsg2 As String = "ListObject filter returned zero records"
Const kMsg3 As String = "Actual record is the last visible record"
Dim wsDATA As Worksheet
Dim lo As ListObject
Dim rTrg As Range
Dim rCllLast As Range
Set wsDATA = ThisWorkbook.Worksheets("DATA")
Set lo = wsDATA.ListObjects("lo.DATA") 'update as required
With lo
On Error Resume Next
Rem Validate Field
Set rTrg = .ListColumns(sFld).DataBodyRange
If rTrg Is Nothing Then
sMsgOut = Replace(kMsg1, "#FLD", sFld)
Exit Function
End If
Rem Validate ListObject AutoFilter
Set rTrg = Nothing
Set rTrg = .ListColumns(sFld).DataBodyRange.SpecialCells(xlCellTypeVisible)
If rTrg Is Nothing Then sMsgOut = kMsg2: Exit Function
On Error GoTo 0
End With
Select Case (rCll Is Nothing)
Case True
Rem No Previous Record
Set rCll = rTrg.Cells(1)
Case False
With lo.ListColumns(sFld).DataBodyRange
Rem Validate Last Record
Set rCllLast = rTrg.Areas(rTrg.Areas.Count).Cells(rTrg.Areas(rTrg.Areas.Count).Cells.Count)
If rCll.Address = rCllLast.Address Then
sMsgOut = kMsg3
Exit Function
Else
Rem Reset Visible Cells Range
Set rTrg = Range(rCll.Offset(1), .Cells(.Cells.Count))
Set rTrg = rTrg.SpecialCells(xlCellTypeVisible)
Rem Set Next Record
Set rCll = rTrg.Cells(1)
End If: End With: End Select
Rem Set Results
Set rOutput = rCll
ListObject_ƒNextVisibleCell = True
End Function
It should be called in this manner
Sub ListObject_ƒNextVisibleCell_TEST()
Const kTitle As String = "ListObject Next Visible Cell"
Dim wsMain As Worksheet, rCll As Range
Dim sFld As String, sMsg As String
sFld = "JobNo"
Set wsMain = ThisWorkbook.Worksheets("Main")
If ListObject_ƒNextVisibleCell(rCll, sMsg, sFld) Then
wsMain.Range("O2").Value2 = rCll.Value2
Else
MsgBox sMsg, vbCritical, kTitle
End If: End With
End Sub
Suggest to check the following pages for details about the resources used:
Worksheet object (Excel)
ListObject object (Excel)
Application.Range property (Excel)
With statement
MsgBox function
I've created a macro to print the range of cells and it's content in the console. The macro is doing just fine. However, the problem is I can't use a button (in another sheet) conected to that macro. To be clearer - I created a macro-enabled button in sheet2 whereas the range of cells I wanaa select and print are within sheet1.
I've tried so far:
Sub LoopAndPrintSelection()
Dim ocel As Range, RangeSelected As Range
Set RangeSelected = Application.Selection
For Each ocel In RangeSelected.Cells
Debug.Print ocel.Address, ocel.value
Next ocel
End Sub
How can I refer the range of selection to any specific sheet?
As others have already mentioned, the "Application.Selection" property will refer to what you have selected in your active sheet. I would recommend that you assign a hotkey to this macro and then you can select the cells you want to print and use the macro's hotkey.
This is one possible solution, but if you need that button on a different sheet and want people to interact with the button (rather than a hotkey) then this won't solve your issue.
This should help with the issue of two different tabs
Sub DUMMY_TEST()
Dim myAREA As Range
Dim mySELECTION As Range
On Error GoTo error_spot
'Stop Excel from "blinking" as tabs are selected/changed and calculating.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set mySELECTION = Application.Selection 'Used to get back to same spot after code has executed
If Sheets("Sheet1").Visible = True Then
Sheets("Sheet1").Activate
Else
'tab not visible, end sub
GoTo error_spot
End If
Set myAREA = Application.Selection
For Each ocel In myAREA.Cells
Debug.Print ocel.Address, ocel.Value
Next ocel
mySELECTION.Worksheet.Activate
mySELECTION.Select
error_spot:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
I have attempted to add functionality to an excel add-in ave been developing which trims the leading spaces at the end of used cells, and maybe even parse the text, The reason I need to do this is simply to have it turn into a hyperlink which I have already working but that parts fine.
This is what I have attempted so far, I have it trimming the active.worksheet am on which is fine but I can't figure out how to:
Trim Every cell being used across the whole workbook.
And also parse the text if possible
This is my attempt at Trimming the entire workbook, Its something simple I just know it, I just cant figure it out:
Sub DoTrim(Wb As Workbook)
Dim cell As Range
Dim str As String
Dim nAscii As Integer
Dim wsh As Worksheet
For Each wsh In Worksheets
With wsh.UsedRange
For Each cell In ActiveSheet.UsedRange
str = Trim(cell)
If Len(str) > 0 Then
nAscii = Asc(Left(str, 1))
If nAscii < 33 Or nAscii = 160 Then
If Len(str) > 1 Then
str = Right(str, Len(str) - 1)
Else
str = ""
End If
End If
End If
cell = str
Next cell
End With
Next wsh
End Sub
Any advice would be welcome am fairly new to this Language so sorry if I sound like a complete Newb!
TL;DR Trims cells only worksheet am on, needs to run across whole workbook I cant figure out how to iterate it across the whole thing.
EDIT: Is that also a quicker way of trimming these cells, the spreadsheets that are created for whom am designing this are massive and takes a while to trim the cells at times
Try this
Sub DoTrim(Wb As Workbook)
Dim aCell As Range
Dim wsh As Worksheet
'~~> If you are using it in an Add-In, it is advisable
'~~> to keep the user posted :)
Application.StatusBar = "Processing Worksheets... Please do not disturb..."
DoEvents
Application.ScreenUpdating = False
For Each wsh In Wb.Worksheets
With wsh
Application.StatusBar = "Processing Worksheet " & _
.Name & ". Please do not disturb..."
DoEvents
For Each aCell In .UsedRange
If Not aCell.Value = "" And aCell.HasFormula = False Then
With aCell
.Value = Replace(.Value, Chr(160), "")
.Value = Application.WorksheetFunction.Clean(.Value)
.Value = Trim(.Value)
End With
End If
Next aCell
End With
Next wsh
Application.ScreenUpdating = True
Application.StatusBar = "Done"
End Sub
I agree with Siddarth:
For Each cell In ActiveSheet.UsedRange
Should be:
For Each cell In wsh.UsedRange
I would have thought you should be able to remove with 'With wsh.UsedRange' statement around the loop as well.
As you are passing in a WorkBook reference, perhaps you should consider changin your outer For loop from:
For Each wsh In Worksheets
to:
For Each wsh In Wb.Worksheets