A group of teachers (I am one) is using a spreadsheet to track missing assignments of students. The students' names are in column A, and missing assignments expand in columns to the right.
When an assignment is missing, the teacher puts their initial in the cell and right-clicks to add a comment about the assignment.
When the student submits the assignment, the teacher changes the cell's fill from nothing (xlNone) to yellow or grey.
We'd like Excel to send us a daily email that lists only the students with missing assignments in cells filled with xlNone along with the initial of the teacher or teachers.
This code does not error. The email object is constructed, but there is no data in the body of the email.
Sub Mail_Selection_Range_Outlook_Body()
'Thanks to code by Tim Williams and Ron de Bruin
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
On Error Resume Next
Dim cell As Range
Dim ci As Long
Set rng = Nothing
For Each cell In Sheet1.Range("C4:Z100").Cells
ci = cell.Interior.ColorIndex
If (ci = -4142 Or ci = 2 Or ci = 15 Or ci = 16) And Not IsNumeric(cell.Value) Then
If rng Is Nothing Then
Set rng = cell
Else
Set rng = Application.Union(rng, cell)
End If
End If
Next cell
If Not rng Is Nothing Then rng.Select
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "teacher1#school.org, teacher2#school.org"
.CC = ""
.BCC = ""
.Subject = "This is the list of students with missing work"
.HTMLBody = RangetoHTML(rng)
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' Thanks to Ron de Bruin's page
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to paste the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
I commented On Error Resume Next out as suggested and get an error
'Type Mismatch'
Looking at the sheet, the various cells are highlighted.
Hello middleschoolteacher,
UPDATED ANSWER
Sub Mail_Selection_Range_Outlook_Body()
'Thanks to code by Tim Williams and Ron de Bruin
' Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
' On Error Resume Next
Dim cell As Range
Dim ci As Long
' Set rng = Nothing
Dim str As String
str = Empty
For Each cell In Sheet1.Range("C4:Z100").Cells
ci = cell.Interior.ColorIndex
If (ci = -4142 Or ci = 2 Or ci = 15 Or ci = 16) And Not IsNumeric(cell.value) Then
'****************************************************************************
' If rng Is Nothing Then
' Set rng = cell
' Else
' Set rng = Application.Union(rng, cell)
' End If
str = str & CStr(cell.value) & " "
'****************************************************************************
End If
Next cell
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
' On Error Resume Next
With OutMail
.To = "teacher1#school.org, teacher2#school.org"
.CC = ""
.BCC = ""
.Subject = "This is the list of students with missing work"
'****************************************************************************
Dim wdDoc As Object
Dim olinsp As Object
Set wdDoc = CreateObject("Word.Document")
Set olinsp = .GetInspector
Set wdDoc = olinsp.WordEditor
If Not IsEmpty(str) Then
wdDoc.Range.InsertBefore str
.Display
.Send
Else
MsgBox prompt:="No cells meet the criteria"
Exit Sub
End If
'****************************************************************************
End With
Set OutMail = Nothing
Set OutApp = Nothing
Set wdDoc = Nothing
Set olinsp = Nothing
End Sub
So, I'm not sure this gives you the intended result, however after testing with a test worksheet I successfully displayed the email with all the relevant cell values separated by a space (you can choose whatever you want to separate the values, you just need to replace the " " on the line containing str = str & CStr(cell.value) & " " .
I changed where the .Send method is on the code so that no email is sent if there are no relevant cells.
I fail to understand how are you going to know which student hasn't yet submitted the assignment as the relevant cells contain only the initial of the teacher? Or am I getting this wrong?
If you need to include the student's name also for each cell value then the code can be easily modified to do that, however I'm not sure that I fully understand what is the desired output is here.
Anyways let me know how it goes.
ADDITION
You can output a single line like this: studentA_J studentB_F studentC_W , J F and W being the initials of the teachers. In order to achieve that you need only the the line containing str = str & CStr(cell.value) & " " and change it to str = str & Sheet1.Cells(cell.row,j) & "_" & CStr(cell.value) & " " , where j needs to be the index of the column where the student's name is.
If I remember correctly you can even write it using the letter of the column, for example if the students names are in column A then you can also replace the above line of code also by str = str & Sheet1.Cells(cell.row,"A") & "_" & CStr(cell.value) & " "
UPGRADED CODE
Sub Mail_Selection_Range_Outlook_Body()
'Thanks to code by Tim Williams and Ron de Bruin
Dim OutApp As Object
Dim OutMail As Object
Dim cell As Range, studentCell As Range
Dim ci As Long
Dim str As String
str = Empty
With Application
.calculation = xlCalculationManual
.DisplayStatusBar = False
.enableEvents = False
.screenUpdating = False
.Interactive = False
.DisplayAlerts = False
.AskToUpdateLinks = False
End With
For Each cell In Sheet1.Range("C4:Z100").Cells
ci = cell.Interior.ColorIndex
If (ci = -4142 Or ci = 2 Or ci = 15 Or ci = 16) And Not IsNumeric(cell.value) Then
'****************************************************************************
Set studentCell = Sheet1.Cells(cell.Row, "A")
With cell
If Not .CommentThreaded Is Nothing Then
str = str & studentCell.value & "_" & CStr(.value) & "_" & .CommentThreaded.Text & vbCrLf
Else
str = str & studentCell.value & "_" & CStr(.value) & vbCrLf
End If
End With
'****************************************************************************
End If
Next cell
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "teacher1#school.org, teacher2#school.org"
.CC = ""
.BCC = ""
.Subject = "This is the list of students with missing work"
'****************************************************************************
Dim wdDoc As Object
Dim olinsp As Object
Set wdDoc = CreateObject("Word.Document")
Set olinsp = .GetInspector
Set wdDoc = olinsp.WordEditor
If Not IsEmpty(str) Then
wdDoc.Range.InsertBefore str
Else
MsgBox prompt:="No cells meet the criteria"
GoTo SafeExit
End If
'****************************************************************************
.Display
.Send
End With
SafeExit:
With Application
.calculation = xlCalculationAutomatic
.DisplayStatusBar = False
.enableEvents = False
.screenUpdating = False
.Interactive = False
.DisplayAlerts = False
.AskToUpdateLinks = False
End With
Set OutMail = Nothing
Set OutApp = Nothing
Set wdDoc = Nothing
Set olinsp = Nothing
End Sub
If you use this upgraded code then it will run much faster.
Related
I want to send template emails based on selections in an Excel worksheet.
I want to choose which email template and subject line from a dropdown selection in the worksheet. If empty, I want to skip.
The range with the dropdown options.
Dim cellrange As Range, cell As Range
Set cellrange = Range("H3:H500")
I use PowerShell and other scripting languages regularly. My VBA exposure is limited.
I created variables for the different email templates and the subject lines. I also found code that will send the emails. That part seems ok.
I receive one email instead of looping through however many emails based on the selections in the sheet.
I set the email templates and subject line in a range in a different sheet and created variables for them.
Set delivery = Sheets("EmailTemplates").Range("A5:A40")
Set address = Sheets("EmailTemplates").Range("A50:A90")
Set deliverysub = Sheets("EmailTemplates").Range("B2")
Set addresssub = Sheets("EmailTemplates").Range("B50")
I will also create this for the Meeting template and subject line. I've been trying to get it to work with these two first and getting varied results.
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Dim delivery As Range
Dim shipping As Range
Dim meeting As Range
Dim address As Range
Dim deliverysub As Range
Dim shippingsub As Range
Dim meetingsub As Range
Dim addresssub As Range
Dim template As Range
Set delivery = Sheets("EmailTemplates").Range("A5:A40")
Set address = Sheets("EmailTemplates").Range("A50:A90")
Set deliverysub = Sheets("EmailTemplates").Range("B2")
Set addresssub = Sheets("EmailTemplates").Range("B50")
Set rng = Nothing
On Error Resume Next
Dim cellrange As Range, cell As Range
Set cellrange = Range("H3:H500")
For Each cell In cellrange
If cell.Value = "" Then
Next cell
ElseIf cell.Value = "Delivery" Then
Set rng = delivery
Set SubjectLine = deliverysub
ElseIf cell.Value = "Shipping" Then
Set rng = address
Set SubjectLine = addresssub
ElseIf cell.Value = "Meeting" Then
Set rng = meeting
Set SubjectLine = meetingsub
End If
Next cell
On Error GoTo 0
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "myemail#email.com"
.CC = ""
.BCC = ""
.Subject = SubjectLine
.HTMLBody = RangetoHTML(rng)
.SEND 'or use .Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set rng = Nothing
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
End Sub
More like this:
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Dim ws As Worksheet, wsTemplates As Worksheet
Dim cell As Range, subj As String
Set ws = ActiveSheet
Set wsTemplates = ThisWorkbook.Worksheets("EmailTemplates")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In ws.Range("H3:H500").Cells
'What type of template are we using?
Select Case cell.Value
Case "Delivery"
Set rng = wsTemplates.Range("A5:A40")
subj = wsTemplates.Range("B2")
Case "Shipping"
Set rng = wsTemplates.Range("A50:A90")
subj = wsTemplates.Range("B50")
Case "Meeting"
'etc etc
Case Else
Set rng = Nothing 'not sending anything
End Select
If Not rng Is Nothing Then 'sending?
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "myemail#email.com"
.CC = ""
.BCC = ""
.Subject = subj
.HTMLBody = RangetoHTML(rng)
.SEND 'or use .Display
End With
End If
Next cell
End Sub
So currently my code works almost to what I like it too.
It currently groups the same emails together and emails that range to the person.
But in the email I'm trying to not include column A which is their email.
For Each cell In WS.Columns("A").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
'check if this email address has been used to generate an outlook email or not
If dict.exists(cell.Value) = False Then
dict.Add cell.Value, "" 'add the new email address
Set OutMail = OutApp.CreateItem(0)
Set rng = WS.UsedRange.Rows(1)
'find all of the rows with the same email and add it to the range
For Each cell2 In WS.UsedRange.Columns(1).Cells
If cell2.Value = cell.Value Then
Set rng = Application.Union(rng, WS.UsedRange.Rows(cell2.Row))
End If
Next cell2
I tried modifying the above code but can't seem to work it out.. Can anyone help me out?
full code:
Option Explicit
Sub Test1()
Dim OutApp As Object
Dim OutMail As Object
Dim dict As Object 'keep the unique list of emails
Dim cell As Range
Dim cell2 As Range
Dim rng As Range
Dim i As Long
Dim WS As Worksheet
Dim Signature As String
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
Set dict = CreateObject("scripting.dictionary")
Set WS = ThisWorkbook.Sheets("Sheet1") 'Current worksheet name
On Error GoTo cleanup
For Each cell In WS.Columns("A").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
'check if this email address has been used to generate an outlook email or not
If dict.exists(cell.Value) = False Then
dict.Add cell.Value, "" 'add the new email address
Set OutMail = OutApp.CreateItem(0)
Set rng = WS.UsedRange.Rows(1)
'find all of the rows with the same email and add it to the range
For Each cell2 In WS.UsedRange.Columns(1).Cells
If cell2.Value = cell.Value Then
Set rng = Application.Union(rng, WS.UsedRange.Rows(cell2.Row))
End If
Next cell2
On Error Resume Next
With OutMail
.SentOnBehalfOfName = ""
.GetInspector ' ## This inserts default signature
Signature = .HTMLBody ' ## Capture the signature HTML
.To = cell.Value
.CC = ""
.Subject = "Reminder"
.HTMLBody = "<BODY style=font-size:12pt;font-family:Calibri><font color=#000000>Hi " & WorksheetFunction.Proper(RemoveNumbers(Left((cell.Value), InStr((cell.Value), ".") - 1))) & ", " & "<br><br>" & "Please see your trip numbers and estimated cost below:" & vbNewLine & vbNewLine & RangetoHTML(rng) & Signature & "</font></BODY>"
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
End If
End If
Next cell
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Sub
Function RemoveNumbers(Txt As String) As String
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "[0-9]"
RemoveNumbers = .Replace(Txt, "")
End With
End Function
Function RangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
One option is to use Intersect and Resize.
After the loop that creates rng but before passing rng to RangetoHTML:
With WS.UsedRange
Set rng = Intersect(rng, .Columns(2).Resize(,.Columns.Count - 1))
End With
I've code to paste specified ranges in different sheets into Outlook email.
I've separate code to hide cells. I only want to paste if there is data in the table.
When the last row of the specific range is hidden, the code fails. If I leave the last row unhidden, it will result in empty rows in the email.
How do I run the code even if the last row within the range is hidden?
Sub Trigger_Email()
'add rng as you add tabs. Remember to add rng under (i) Set rng and also
(ii) With OutMail
Dim rng As Range 'For TAB01 Tab
Dim rng2 As Range 'For TAB02 Tab
Dim rng3 As Range 'For TAB03 Tab
Dim rng4 As Range 'For TAB04 Tab
Dim rng5 As Range 'For TAB05 Tab
Dim OutApp As Object
Dim OutMail As Object
Dim StrBody As String
'Shows what appears at top of email
StrBody = "Hi XXX," & "<br>" & "<br>" & _
"The company provides" & "<br>" & "<br>" & _
"For your consideration& views." & "<br>" & "<br>"
Set rng = Nothing
On Error Resume Next
'This determines range to be printed into email.
'ADD rng(n+1) hear if you increase cover type. Determine range here as well.
Set rng =
Sheets("TAB01").Range("A5:G22").Rows.SpecialCells(xlCellTypeVisible)
Set rng2 =
Sheets("TAB02").Range("A1:F39").Rows.SpecialCells(xlCellTypeVisible)
Set rng3 =
Sheets("TAB03").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
Set rng4 =
Sheets("TAB04").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
Set rng5 =
Sheets("TAB05").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
'add more rng tabs below to display in body of email.
With OutMail
.To = "TEST#HOTMAIL.COM"
.CC = ""
.BCC = ""
.Subject = "TEST 01" & Cells(5, 1)
.HTMLBody = StrBody & rangetoHTML(rng) & rangetoHTML(rng3) &
rangetoHTML(rng4) & rangetoHTML(rng5) & rangetoHTML(rng2)
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
'Ignore this section. It prints excel format into HTML format in email.
Function rangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TemTAB05B As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TemTAB05B = Workbooks.Add(1)
With TemTAB05B.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TemTAB05B.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TemTAB05B.Sheets(1).Name, _
Source:=TemTAB05B.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
rangetoHTML = ts.readall
ts.Close
rangetoHTML = Replace(rangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TemTAB05B
TemTAB05B.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TemTAB05B = Nothing
End Function
I want to paste a range of cells in Outlook.
Here is my code:
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
' Only send the visible cells in the selection.
Set rng = Selection.SpecialCells(xlCellTypeVisible)
Set rng = Sheets("Sheet1").RangeToHtml("D4:D12").SpecialCells(xlCellTypeVisible, xlTextValues)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected. " & _
vbNewLine & "Please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = ThisWorkbook.Sheets("Sheet2").Range("C1").Value
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangeToHtml.rng
' In place of the following statement, you can use ".Display" to
' display the e-mail message.
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
I am not getting any error, it just does not paste range in Outlook.
I have removed the On Error Resume Next. It gives me an error
Object doesn't support this property or method.
First off, RangeToHTML. The script calls it like a method, but it isn't. It's a popular function by MVP Ron de Bruin. Coincidentally, that links points to the exact source of the script you posted, before those few lines got b̶u̶t̶c̶h̶e̶r̶e̶d̶ modified.
On with Range.SpecialCells. This method operates on a range and returns only those cells that match the given criteria. In your case, you seem to be only interested in the visible text cells. Importantly, it operates on a Range, not on HTML text.
For completeness sake, I'll post a working version of the script below. I'd certainly advise to disregard it and revisit the excellent original by Ron the Bruin.
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
' Only send the visible cells in the selection.
Set rng = Sheets("Sheet1").Range("D4:D12").SpecialCells(xlCellTypeVisible)
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected. " & _
vbNewLine & "Please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = ThisWorkbook.Sheets("Sheet2").Range("C1").Value
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(rng)
' In place of the following statement, you can use ".Display" to
' display the e-mail message.
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' By Ron de Bruin.
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.ReadAll
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Often this question is asked in the context of Ron de Bruin's RangeToHTML function, which creates an HTML PublishObject from an Excel.Range, extracts that via FSO, and inserts the resulting stream HTML in to the email's HTMLBody. In doing so, this removes the default signature (the RangeToHTML function has a helper function GetBoiler which attempts to insert the default signature).
Unfortunately, the poorly-documented Application.CommandBars method is not available via Outlook:
wdDoc.Application.CommandBars.ExecuteMso "PasteExcelTableSourceFormatting"
It will raise a runtime 6158:
But we can still leverage the Word.Document which is accessible via the MailItem.GetInspector method, we can do something like this to copy & paste the selection from Excel to the Outlook email body, preserving your default signature (if there is one).
Dim rng as Range
Set rng = Range("A1:F10") 'Modify as needed
With OutMail
.To = "xxxxx#xxxxx.com"
.BCC = ""
.Subject = "Subject"
.Display
Dim wdDoc As Object '## Word.Document
Dim wdRange As Object '## Word.Range
Set wdDoc = OutMail.GetInspector.WordEditor
Set wdRange = wdDoc.Range(0, 0)
wdRange.InsertAfter vbCrLf & vbCrLf
'Copy the range in-place
rng.Copy
wdRange.Paste
End With
Note that in some cases this may not perfectly preserve the column widths or in some instances the row heights, and while it will also copy shapes and other objects in the Excel range, this may also cause some funky alignment issues, but for simple tables and Excel ranges, it is very good:
I have a VBA code that is copying a range in Excel and pasting into the body of an Outlook email. The code works on several of my colleagues computers but not mine. The code gets as far as creating the .temp file, populating the To:, CC: and Subject but nothing appears in the body. I am trying to paste HTML. I am thinking it's a setting or something like that, but I'm not sure where to start. Any help on this would be greatly appreciated.
Sub CreateEmail()
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
datestr = Date
sbj = "***"
toStr = "****"
Ccstr = "*****"
Dim rng As Range
Set rng = Nothing
Set rng = Range("Flash")
On Error Resume Next
With OutMail
.To = toStr
.Display
.CC = Ccstr
.BCC = ""
.Subject = sbj & datestr
.HTMLBody = RangetoHTML(rng)
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
Application.ScreenUpdating = False
TempFile = Environ$("temp") & "/" & "temp" & ".htm"
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.ReadAll
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
TempWB.Close savechanges:=False
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
Application.ScreenUpdating = True
End Function
I opted to used this method instead. This pastes a range of excel, but not the HTML version. I did not need to use HTML. This works just as good, but without colors and such.
Sub SendFlash()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
Set rng = Range("K4:L8").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
toStr = "*"
Ccstr = "*"
sbj = "Tran"
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = toStr
.CC = Ccstr
.BCC = ""
.Subject = sbj & Date
.Body = TableRangeToTabDelim(rng)
.display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function TableRangeToTabDelim(TableRange As Range) As String
Dim TableCols As Integer
Dim TableRows As Integer
Dim ReturnString As String
TableCols = TableRange.Columns.Count
TableRows = TableRange.Rows.Count
i = 1
j = 1
Do While i <= TableRows
Do While j <= TableCols
ReturnString = ReturnString & TableRange.Cells(i, j).Value
If j <> TableCols Then
ReturnString = ReturnString & Chr(9)
End If
j = j + 1
Loop
j = 1
If i <> TableRows Then
ReturnString = ReturnString & Chr(10)
End If
i = i + 1
Loop
TableRangeToTabDelim = ReturnString
End Function