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
Related
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.
The ask: to extract information from Excel, send the info to respective email addresses on Outlook based on the names in a column.
Eg: if cell contains "Mary", copy the entire table and send to Mary's email address.
I keep the list of names and their respective email addresses in a separate sheet.
This is the information, assuming range is A1:D5
This is the list of email addresses, in a separate sheet
Updated: Long data
Desired output for Mary
The idea: In the data table,
i) i want to look up row 2 (names) for every name,
ii) copy the information about that person into Outlook.
Eg:
For Mary, copy A1: C5 and send to Mary's email address.
For Tom, copy headers (A1:A5) and Tom's data (D1:D5), excluding Mary's data, and send to Tom's email address.
I am stuck on how to do a loop search. I only have the basic macro to extract data and send to Outlook with no name reference:
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("A1:D5").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 = "mary#123.com"
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(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 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
You could loop through the columns (from right to left) and hide the unwanted ones.
update1 - restrict to 5 rows
update2 - don't send if no column visible
update3 - reformat added
Option Explicit
Sub Mail_Selection_Range_Outlook_Body()
Dim wb As Workbook
Dim wsUser As Worksheet, wsTable As Worksheet, wsEmail As Worksheet
Dim rng As Range, n As Long
Dim lastcol As Long, lastrow As Long, i As Long, c As Long
Dim sName As String, sAddr As String
Set wb = ThisWorkbook
Set wsTable = wb.Sheets("Sheet1")
Set wsEmail = wb.Sheets("Email")
With wsTable
lastcol = .Cells(1, Columns.Count).End(xlToLeft).Column
End With
Dim OutApp As Object, OutMail As Object, bSend As Boolean
Set OutApp = CreateObject("Outlook.Application")
' for each user
Set wsUser = wb.Sheets("Sheet2")
With wsUser
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
sName = .Cells(i, "A")
sAddr = .Cells(i, "B")
bSend = False
' hide columns for not name
wsTable.Columns.Hidden = False
For c = lastcol To 2 Step -1
If wsTable.Cells(2, c).Value2 <> sName _
Or wsTable.Cells(3, c).Value2 = "Beef" Then
wsTable.Columns(c).Hidden = True
Else
bSend = True
End If
Next
' send email
If bSend Then
' visible
' copy to email sheet
Set rng = wsTable.UsedRange.Rows("1:5").SpecialCells(xlCellTypeVisible)
wsEmail.Cells.Clear
rng.Copy wsEmail.Range("A1")
Set rng = Reformat(wsEmail)
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = sAddr
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(rng)
.Display
End With
wsEmail.Cells.Clear
n = n + 1
End If
Next
End With
wsTable.Columns.Hidden = False
Set OutMail = Nothing
Set OutApp = Nothing
MsgBox n & " emails sent", vbInformation
End Sub
Function Reformat(ws) As Range
Const MAX = 4
Dim lastrow As Long, lastcol As Long
Dim r As Long, c As Long
With ws
lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
r = lastrow + 2
c = MAX + 2
Do While c <= lastcol
.Cells(1, 1).Resize(lastrow).Copy .Cells(r, 1)
.Cells(1, c).Resize(lastrow, MAX).Copy .Cells(r, 2)
c = c + MAX
r = r + lastrow + 2
Loop
.Columns(MAX + 2).Resize(, lastcol).Delete
End With
Set Reformat = ws.UsedRange
End Function
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 have to send an email that includes a table, in the body, that was copied from a filter table.
The filter table's name is "ds" in this code.
I use function RangetoHTML (code below) but it only copies the format, not the contents of the table:
Sub Email_Syndicate()
Dim OutApp As Object
Dim OutMail As Object
Dim EmailSubject As String
Dim EmailSendTo As String
Dim MailBody As String
Dim cell As Range
Dim Signature As String
Dim ds As Range
Set rng = Range(Range("B2"), Range("B" & Rows.Count).End(xlUp))
For Each cell In rng
rw = cell.Row
If cell.Value <> "" Then
EmailSendTo = cell.Value
Nme = cell.Offset(0, 3).SpecialCells(xlCellTypeVisible)
xCC = cell.Offset(0, 1)
att = cell.Offset(0, 4).Value
EmailSubject = cell.Offset(0, 2)
lr1 = Sheet3.Cells(Rows.Count, 1).End(xlUp).Row
Sheet3.Range("A1:N" & lr1).AutoFilter field:=6, Criteria1:=Sheet4.Range("F2").Value
lr = Sheet3.Cells(Sheet3.Rows.Count, 1).End(xlUp).Row
Set ds = Sheet3.Range("A1:N" & lr).SpecialCells(xlCellTypeVisible)
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(o)
With OutMail
.Display
End With
Signature = OutMail.HTMLBody
'On Error Resume Next
With OutMail
.Subject = EmailSubject
.To = EmailSendTo
.CC = xCC
'.Body = MailBody
.HTMLBody = RangetoHTML(ds)
.Display
.send
End With
Set OutMail = Nothing
Set OutApp = Nothing
MailBody = ""
End If
With Application
.EnableEvents = 1
.Calculation = xlCalculationAutomatic
End With
Set OutMail = Nothing: Set OutApp = Nothing
Next
End Sub
Function RangetoHTML(rng As Range)
Dim fso As Object, ts As Object, TempWB As Workbook, TempFile As String
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=xlPasteAll
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteColumnWidths, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
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 0
Kill TempFile
Set ts = Nothing: Set fso = Nothing: Set TempWB = Nothing
End Function
How can I copy a filter table containing full content to the email body?
Sub SendEmail_1()
Dim outlook As Object
Dim newEmail As Object
Dim xInp As Object
Dim pgEdit As Object
Dim pos As Integer
Set outlook = CreateObject("Outlook.Application")
Set newEmail= outlook.Createitem(o)
'construction email
With newEmail
.Recipients.Add ("person#email.com")
.Subject = "Subject goes here"
Set xInp = newEmail.GetInspector 'gets you into the test editor
Set pgEdit = xInp.WordEditor 'returns a word document object you can edit
'selects data we want to copy into email
Sheets("your Sheet").Range("your Range").Copy
'pastes the excel range over the indicator
pgEdit.Range(Start:=0, End:=1).PasteSpecial Placement:=wdInLine
Application.CutCopyMode = True
.display
.send
End With
End Sub
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: