Excel VBA - Shapes.AddPicture vs Pictures.Insert into spreadsheet from mapped drive - excel

I have been using an Excel VBA macro to add images to my spreadsheet lists from a folder on our office server. The list exports from my database software with the containing folder and image name in Column A (e.g. 038/19761809.jpg). I now need to send these documents to persons outside of my office without access to our server so I am trying to switch from using ActiveSheet.Pictures.Insert to using the more correct ActiveSheet.Shapes.AddPicture. The goal is to have the image files embed in the document rather than just linking to the files on our office server.
This code (using Pictures.Insert) inserts the images as links. When I email the spreadsheet to off-site users, the linked images "break" as recipient's computer cannot find them (because their computer is not on our local network).
Sub InsertPictures()
Dim MyRange As String
Dim picname As String
Dim mySelectRange As String
Dim rcell As Range
Dim IntInstr As Integer
Dim Mypath As String
Mypath = "S:\pp4\images\"
MyRange = "A2:A275"
Range(MyRange).Select
For Each rcell In Selection.Cells
If Len(rcell.Value) > 0 Then
picname = Mypath & rcell.Value
mySelectRange = Replace(MyRange, "B", "A")
IntInstr = InStr(mySelectRange, ":")
mySelectRange = Left(mySelectRange, IntInstr - 1)
do_insertPic picname, mySelectRange, rcell.Left, rcell.Top
End If
Next
Application.ScreenUpdating = True
End Sub
Sub do_insertPic(ByRef picname As String, ByRef MyRange As String, myleft As Integer, mytop As Integer)
Dim rcell As Range
Range(MyRange).Select
On Error GoTo ErrNoPhoto
ActiveSheet.Pictures.Insert(picname).Select
On Error GoTo 0
With Selection
.Left = myleft + 4
.Top = mytop + 4
.ShapeRange.LockAspectRatio = msoTrue
.ShapeRange.Height = 115#
.ShapeRange.Rotation = 0#
End With
Exit Sub
ErrNoPhoto:
MsgBox "Unable to Find Photo" 'Shows message box if picture not found
End Sub
I have modified my code to use the formatting for Shapes.AddPicture. Here is the new code:
Sub InsertPictures()
Dim MyRange As String
Dim picname As String
Dim mySelectRange As String
Dim rcell As Range
Dim IntInstr As Integer
Dim Mypath As String
Mypath = "S:\pp4\images\"
MyRange = "A2:A275"
Range(MyRange).Select
For Each rcell In Selection.Cells
If Len(rcell.Value) > 0 Then
picname = Mypath & rcell.Value
mySelectRange = Replace(MyRange, "B", "A")
IntInstr = InStr(mySelectRange, ":")
mySelectRange = Left(mySelectRange, IntInstr - 1)
do_insertPic picname, mySelectRange, rcell.Left, rcell.Top
End If
Next
Application.ScreenUpdating = True
End Sub
Sub do_insertPic(ByRef picname As String, ByRef MyRange As String, myleft As Integer, mytop As Integer)
Dim rcell As Range
Range(MyRange).Select
On Error GoTo ErrNoPhoto
ActiveSheet.Shapes.AddPicture(Filename:=picname, LinkToFile:=msoFalse, SaveWithDocument:=msoTrue, Left:=myleft + 4, Top:=mytop + 4, LockAspectRatio:=msoTrue, Height:=115#, Rotation:=0#).Select
On Error GoTo 0
Exit Sub
ErrNoPhoto:
MsgBox "Unable to Find Photo" 'Shows message box if picture not found
End Sub
When I try to run the new macro Excel just puts up my "Unable to Find Photo" error message. Can you help me find what I did wrong? Thanks for any help!

You have 2 extra arguments in Shapes.AddPicture (LockAspectRatio, Rotation), and a missing one (Width).
See more details about Shapes.AddPicture, and your corrected code below:
Sub do_insertPic(ByRef picname As String, ByRef MyRange As String, myleft As Integer, mytop As Integer)
Dim sht As Worksheet: Set sht = ActiveSheet
Dim rcell As Range
Range(MyRange).Select
On Error GoTo ErrNoPhoto
With sht.Shapes
.AddPicture _
Filename:=picname, _
LinkToFile:=msoFalse, _
SaveWithDocument:=msoTrue, _
Left:=myleft + 4, _
Top:=mytop + 4, _
Width:=-1, _
Height:=115
End With
On Error GoTo 0
Exit Sub
ErrNoPhoto:
Debug.Print "Unable to Find Photo" 'Shows message box if picture not found
End Sub
PS: I recommend you to read about avoiding to use .Select in everything...

Related

Multiple find and replace in PowerPoint with Excel sheet

I am trying to customize the script in the webpage below to find and replace words and phrases in a PowerPoint document using a pre-defined Excel list. The code below worked best for me but I need to use with long replacement lists. I have tried many times but failed to get the correct array.
The excel list is very long and has two columns with headers: "Find what" and "Replace with"
Excel document name: Offices.xlsx
Path: C:\Users\JL\Docuemnts
Sheet name: Sheet1
I get this error: (Run-time error '424': Object required) at this line:
Set wb = Workbooks.Open("C:\Users\JL\Docuemnts\Offices.xlsx")"
Any help would be much appreciated.
Thank you
(Source script: https://www.msofficeforums.com/powerpoint/20104-find-replace-macro.html)
Sub PPTFindAndReplace()
Dim oPres As Presentation
Dim oSld As Slide
Dim oShp As Shape
Dim FindWhat As String
Dim ReplaceWith As String
Dim x As Integer
Set wb = Workbooks.Open("C:\Users\JL\Docuemnts\Offices.xlsx")
myArray = Workbook.Sheets("Sheet1").Range("a2:a200").Value
myArray2 = Workbook.Sheets("Sheet1").Range("b2:b200").Value
FindWhat = myArray(x)
ReplaceWith = myArray2(x)
For Each oPres In Application.Presentations
For Each oSld In oPres.Slides
For Each oShp In oSld.Shapes
Call ReplaceText(oShp, FindWhat, ReplaceWith)
Next oShp
Next oSld
Next oPres
End Sub
Sub ReplaceText(oShp As Object, FindString As String, ReplaceString As String)
Dim oTxtRng As TextRange
Dim oTmpRng As TextRange
Dim I As Integer
Dim iRows As Integer
Dim iCols As Integer
Dim oShpTmp As Shape
' Always include the 'On error resume next' statement below when you are working with text range object.
' I know of at least one PowerPoint bug where it will error out - when an image has been dragged/pasted
' into a text box. In such a case, both HasTextFrame and HasText properties will return TRUE but PowerPoint
' will throw an error when you try to retrieve the text.
On Error Resume Next
Select Case oShp.Type
Case 19 'msoTable
For iRows = 1 To oShp.Table.Rows.Count
For iCol = 1 To oShp.Table.Rows(iRows).Cells.Count
Set oTxtRng = oShp.Table.Rows(iRows).Cells(iCol).Shape.TextFrame.TextRange
Set oTmpRng = oTxtRng.Replace(FindWhat:=FindString, _
ReplaceWhat:=ReplaceString, WholeWords:=False)
Do While Not oTmpRng Is Nothing
Set oTmpRng = oTxtRng.Replace(FindWhat:=FindString, _
ReplaceWhat:=ReplaceString, _
After:=oTmpRng.Start + oTmpRng.Length, _
WholeWords:=False)
Loop
Next
Next
Case msoGroup 'Groups may contain shapes with text, so look within it
For I = 1 To oShp.GroupItems.Count
Call ReplaceText(oShp.GroupItems(I), FindString, ReplaceString)
Next I
Case 21 ' msoDiagram
For I = 1 To oShp.Diagram.Nodes.Count
Call ReplaceText(oShp.Diagram.Nodes(I).TextShape, FindString, ReplaceString)
Next I
Case Else
If oShp.HasTextFrame Then
If oShp.TextFrame.HasText Then
Set oTxtRng = oShp.TextFrame.TextRange
Set oTmpRng = oTxtRng.Replace(FindWhat:=FindString, _
ReplaceWhat:=ReplaceString, WholeWords:=False)
Do While Not oTmpRng Is Nothing
Set oTmpRng = oTxtRng.Replace(FindWhat:=FindString, _
ReplaceWhat:=ReplaceString, _
After:=oTmpRng.Start + oTmpRng.Length, _
WholeWords:=False)
Loop
End If
End If
End Select
End Sub

VBA Vlookup Crashing

My VBA Vlookup code is crashing Excel and takes forever to execute. I need the VBA code and not the formula in the cell.
Private Sub Worksheet_Change(ByVal Target As Range)
Call lookup
End Sub
Sub lookup()
Dim srchres As Variant
Dim srch As Variant
Set sh1 = ThisWorkbook.Sheets("Customer")
Set sh4 = ThisWorkbook.Sheets("Invoice")
On Error Resume Next
srchres = Application.WorksheetFunction.VLookup(sh4.Range("A11:C11"), _
sh1.Range("B2:H99999"), 5, False)
On Error GoTo 0
If (IsEmpty(srchres)) Then
sh4.Range("A12") = CVErr(xlErrNA)
Else
sh4.Range("A12:C12").Value = srchres
End If
On Error Resume Next
srch = Application.WorksheetFunction.VLookup(sh4.Range("A11:C11"), _
sh1.Range("B2:H99999"), 6, False)
On Error GoTo 0
If (IsEmpty(srch)) Then
sh4.Range("A13:C13") = CVErr(xlErrNA)
Else
sh4.Range("A13:C13").Value = srch
End If
End Sub
ThisWorkbook's code for creating a drop-down list
Private Sub Worksheet_Change(ByVal Target As Range)
Dim MySheets As Variant
Dim srchres As Variant
Dim srch As Variant
Set sh1 = ThisWorkbook.Sheets("Customer")
Set sh4 = ThisWorkbook.Sheets("Invoice")
MySheets = Array("sh1", "sh4")
If IsNumeric(Application.match(Sh.Name, MySheets, 0)) Then
If Target.Address = "'sh4'!$A$11" Then
sh4.Range("A2:A9999").Cells(sh4.Range("A2:A9999").Rows.Count + 1, 1) = Target
End If
End If
End Sub

Copying images in an Excel file into a Word table

I am using Office 365 on a Windows 10 64-bit pc.
I have a Word document with a table into which I want to copy elements from an Excel document. The elements are a) text from its cell, b) a hyperlink from its cell and c) images from a list of images.
The first two tasks are performed successfully by the following sub:
Sub ImportFromExcel()
Dim RowNo As Long, RowTarget As Long
Dim RowFirst As Long, RowLast As Long
Dim strContent As String, strLink As String, strDisplay As String
Dim xlAppl As New Excel.Application
Dim xlBook As New Excel.Workbook
Dim xlSheet As New Excel.Worksheet
Dim ExcelFileName As String
Dim tbl As Word.Table
On Error GoTo Finish
ExcelFileName = "C:\MyPath\MyExcelDoc.xlsm"
Set xlAppl = CreateObject("Excel.Application")
xlAppl.Application.Visible = False
xlAppl.Workbooks.Open ExcelFileName
Set xlBook = xlAppl.ActiveWorkbook
Set xlSheet = xlBook.Worksheets("Titan")
Set tbl = ActiveDocument.Tables(1)
RowFirst = 6: RowLast = 19
For RowNo = RowFirst To RowLast
RowTarget = RowNo - RowFirst + 1
strContent = xlSheet.Cells(RowNo, 5).Value
tbl.Cell(RowTarget, 1).Range.Text = strContent
strDisplay = xlSheet.Cells(RowNo, 3).Value
tbl.Cell(RowTarget, 3).Range.Text = strContent
strLink = xlSheet.Cells(RowNo, 3).Hyperlinks(1).Address
InsertHyperlinkInTable tbl, RowTarget, 3, strLink, strDisplay
' CopyImageFromExcelToWord xlSheet, RowTarget, tbl
Next RowNo
Finish:
xlAppl.ActiveWorkbook.Close False ' Word will not freeze at this point
xlAppl.Quit
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlAppl = Nothing
End Sub
I copy the hyperlink by reading its address and caption and then recreating it in Word.
Also from Word I can select a give image by way of its index using the first two active lines of the following sub:
Sub CopyImageFromExcelToWord(xlSheet As Excel.Worksheet, imgNo As Long, tbl As Word.Table)
Dim strId As String
' Syntax at https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.select
strId = "Picture " & CStr(2 * imgNo)
xlSheet.Shapes.Range(Array(strId)).Select
' Missing link !
With tbl.Cell(1, 4)
.Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.VerticalAlignment = wdCellAlignVerticalCenter
.Select
End With
Selection.PasteAndFormat (wdFormatOriginalFormatting)
End Sub
An image residing in the clipboard can be inserted into Word using the last six lines.
But I have not found out how to copy the image I selected in the Excel document to the clipboard with a Word macro.
Can this be done somehow?
Can the copying of the hyperlink be performed in a smarter way?
Try
Sub CopyImageFromExcelToWord(xlSheet As Excel.Worksheet, imgNo As Long, tbl As Word.Table)
Dim strId As String
' Syntax at https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.select
strId = "Picture " & CStr(2 * imgNo)
xlSheet.Shapes.Range(Array(strId)).Item(1).Copy
With tbl.Cell(1, 4)
.Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
.VerticalAlignment = wdCellAlignVerticalCenter
.Range.PasteAndFormat wdFormatOriginalFormatting
End With
End Sub

Creating New Sheets with Names from a List

I am pretty new to VBA and am having an issue with my code. I have different hotel names from cell B4 to B27. My goal is to create new worksheets and name each one with the hotel names (going down the list). I tried running the sub procedure below but I am getting an error. The error says:
"Run-time error '1004': Application-defined or object-defined error"
It refers to the line below my comment. Any thoughts on why this is occurring and how I can fix this?
Sub sheetnamefromlist()
Dim count, i As Integer
count = WorksheetFunction.CountA(Range("B4", Range("B4").End(xlDown)))
i = 4
Do While i <= count
' next line errors
Sheets.Add(after:=Sheets(Sheets.count)).Name = Sheets("LocalList").Cells(i, 2).Text
i = i + 1
Loop
Sheets("LocalList").Activate
End Sub
Here is something that I quickly wrote
Few things
Do not find last row like that. You may want to see THIS
Do not use .Text to read the value of the cell. You may want to see What is the difference between .text, .value, and .value2?
Check if the sheet exists before trying to create one else you will get an error.
Is this what you are trying?
Option Explicit
Sub sheetnamefromlist()
Dim ws As Worksheet, wsNew As Worksheet
Dim lRow As Long, i As Long
Dim NewSheetName As String
'~~> Set this to the relevant worksheet
'~~> which has the range
Set ws = ThisWorkbook.Sheets("LocalList")
With ws
'~~> Find last row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row
'~~> Loop through the range
For i = 4 To lRow
NewSheetName = .Cells(i, 2).Value2
'~~> Check if there is already a worksheet with that name
If Not SheetExists(NewSheetName) Then
'~~> Create the worksheet and name it
With ThisWorkbook
.Sheets.Add(After:=.Sheets(.Sheets.Count)).Name = NewSheetName
End With
End If
Next i
End With
End Sub
'~~> Function to check if the worksheet exists
Private Function SheetExists(shName As String) As Boolean
Dim shNew As Worksheet
On Error Resume Next
Set shNew = ThisWorkbook.Sheets(shName)
On Error GoTo 0
If Not shNew Is Nothing Then SheetExists = True
End Function
My assumptions
All cells have valid values i.e which can be used for sheet names. If not, then you will have to handle that error as well.
Workbook (not worksheet) is unprotected
Try,
Sub test()
Dim vDB As Variant
Dim rngDB As Range
Dim Ws As Worksheet, newWS As Worksheet
Dim i As Integer
Set Ws = Sheets("LocalList")
With Ws
Set rngDB = .Range("b4", .Range("b4").End(xlDown))
End With
vDB = rngDB 'Bring the contents of the range into a 2D array.
For i = 1 To UBound(vDB, 1)
Set newWS = Sheets.Add(after:=Sheets(Sheets.Count))
newWS.Name = vDB(i, 1)
Next i
End Sub
Create Worksheets from List
The following will create (and count) only worksheets with valid names.
When the worksheet is already added and the name is invalid, it will be deleted (poorly handled, but it works.)
It is assumed that the list is contiguous (no empty cells).
The Code
Option Explicit
Sub SheetNameFromList()
Const wsName As String = "LocalList"
Const FirstCell As String = "B4"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim ListCount As Long
ListCount = WorksheetFunction.CountA(ws.Range(FirstCell, _
ws.Range(FirstCell).End(xlDown)))
Dim fRow As Long: fRow = ws.Range(FirstCell).Row
Dim fCol As Long: fCol = ws.Range(FirstCell).Column
Dim i As Long, wsCount As Long
Do While i < ListCount
If addSheetAfterLast(wb, ws.Cells(fRow + i, fCol).Value) = True Then
wsCount = wsCount + 1
End If
i = i + 1
Loop
ws.Activate
MsgBox "Created " & wsCount & " new worksheet(s).", vbInformation
End Sub
Function addSheetAfterLast(WorkbookObject As Workbook, _
SheetName As String) _
As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = WorkbookObject.Worksheets(SheetName)
If Err.Number = 0 Then Exit Function
Err.Clear
WorkbookObject.Sheets.Add After:=WorkbookObject.Sheets(Sheets.count)
If Err.Number <> 0 Then Exit Function
Err.Clear
WorkbookObject.ActiveSheet.Name = SheetName
If Err.Number <> 0 Then
Application.DisplayAlerts = False
WorkbookObject.Sheets(WorkbookObject.Sheets.count).Delete
Application.DisplayAlerts = False
Exit Function
End If
addSheetAfterLast = True
End Function

Use ComboBox input to find itself in the workbook?

I am new to VBA and UserForms.
I have a ComboBox where the user will enter a unique Sales Order # (SalesOrder). I want my form to take this input and find it in the workbook and then update the status with the user's inputs in later ComboBoxes (CommentBox & OrderStatus). The issue I am facing is the actual code to find the Sales Order # in the workbook. I've tried what is seen below in several different variations.
If I replace all the ComboBox inputs with the actual inputs as a string, the code runs fine in a module.
Ideally, the code will loop through the sheet array finding all the lines with the Sales Order # and apply the inputs to the row.
Sub AddDataToList()
Dim shtarray As Variant, shtname As Variant
Dim Data As Worksheet, ws As Worksheet
Dim wbk As Workbook
Dim Strg As String
shtarray = Array("EMAUX", "Irene", "Cassandra", "Patricia", "EMREL", "Maria", "Jason", "Peedie", "MICRO", "PARTS", "NAVY", "DELTA")
Set wbk = ThisWorkbook
For Each shtname In shtarray
Set ws = Nothing
On Error Resume Next
Set ws = wbk.Worksheets(shtname)
On Error GoTo 0
If Not (ws Is Nothing) Then
ActiveSheet.Cells.Find(StatusUpdateForm.SalesOrder.Text).Offset(0, 17).Select
ActiveCell.Value = CommentBox.Text
ActiveCell.Offset(0, 2).Value = OrderStatus.Text
End If
Next
MsgBox SalesOrder.Value & "was updated."
End Sub
Thank you for the assistance!
More Information ***
Below is the code for the Update command button. This is a standard two button system, one updates the records and the other cancels the form.
Private Sub UpdateButton_Click()
If Not EverythingFilledIn Then Exit Sub
Me.Hide
AddDataToList
Unload Me
End Sub
And code for the EverthingFilledIn
Private Function EverythingFilledIn() As Boolean
Dim ctl As MSForms.Control
Dim AnythingMissing As Boolean
EverthingFilledIn = True
AnythingMissing = False
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.TextBox Or TypeOf ctl Is MSForms.ComboBox Then
If ctl.Value = "" Then
ctl.BackColor = rgbPink
Controls(ctl.Name & "Label").ForeColor = rgbRed
If Not AnythingMissing Then ctl.SetFocus
AnythingMissing = True
EverythingFilledIn = False
End If
End If
Next ctl
End Function
Try this (my first comment notwithstanding):
Sub AddDataToList()
Dim shtarray As Variant, shtname As Variant
Dim Data As Worksheet, ws As Worksheet
Dim wbk As Workbook
Dim Strg As String
Dim r As Range
shtarray = Array("EMAUX", "Irene", "Cassandra", "Patricia", "EMREL", "Maria", "Jason", "Peedie", "MICRO", "PARTS", "NAVY", "DELTA")
Set wbk = ThisWorkbook
For Each shtname In shtarray
Set ws = wbk.Worksheets(shtname)
Set r = ws.Cells.Find(StatusUpdateForm.SalesOrder.Text) 'better to specify all parameters
If Not r Is Nothing Then
r.Offset(0, 17).Value = CommentBox.Text
r.Offset(0, 2).Value = OrderStatus.Text
End If
Next
MsgBox SalesOrder.Value & "was updated."
End Sub
There is no need to select things.

Resources