How to only paste visible cells in an email body - excel

I am trying to copy an entire sheet into an email body and the sheet is already filtered and hides rows. I want to copy only the visible rows into the email. I thought my code would do that but when the people reply to the emails, the entire sheet (both hidden and unhidden) appears in the email. Any ideas?
Sub Send_Range_Or_Whole_Worksheet_with_MailEnvelope()
'Working in Excel 2002-2013
Dim AWorksheet As Worksheet
Dim Sendrng As Range
Dim rng As Range
On Error GoTo StopMacro
With Application
.ScreenUpdating = False
.EnableEvents = False
.Application.DisplayAlerts = False
End With
'Fill in the Worksheet/range you want to mail
'Note: if you use one cell it will send the whole worksheet
Set Sendrng = Worksheets("Test").Range("A1").SpecialCells(xlCellTypeVisible)
'Remember the activesheet
Set AWorksheet = ActiveSheet
With Sendrng
' Select the worksheet with the range you want to send
.Parent.Select
'Remember the ActiveCell on that worksheet
Set rng = ActiveCell
'Select the range you want to mail
.Select
' Create the mail and send it
ActiveWorkbook.EnvelopeVisible = True
With .Parent.MailEnvelope
' Set the optional introduction field thats adds
' some header text to the email body.
.Introduction = "Test"
With .Item
.To = "test#email.com"
.CC = ""
.BCC = ""
.Subject = "Test"
.Send
End With
End With
'select the original ActiveCell
rng.Select
End With
This was essentially taken from this Example 2 of Ron de Bruin, with some code from another example.

The code below seems to work.
You will have to fill it in with Ranges selection/activation and other details as needed.
EDIT The final step is sending the email (as per an added request of the OP). DoEvents added thanks to an answer to Excel VBA: Sent Outlook email does not include pasted Range
Sub SendEmail()
Dim OutlookApp As Object
'Dim OutlookApp As Outlook.Application
Dim MItem As Object
'Dim MItem As Outlook.MailItem
'Create Outlook object
Set OutlookApp = CreateObject("Outlook.Application")
'Set OutlookApp = New Outlook.Application
Dim Sendrng As Range
Set Sendrng = Worksheets("Test").Range("A1").SpecialCells(xlCellTypeVisible)
Sendrng.Copy
'Create Mail Item
Set MItem = OutlookApp.CreateItem(0)
'Set MItem = OutlookApp.CreateItem(olMailItem)
With MItem
.To = "test#email.com"
.Subject = "Test"
.CC = ""
.BCC = ""
'.Body = "a"
.Display
End With
SendKeys "^({v})", True
DoEvents
With MItem
.Send
End With
Set OutlookApp = Nothing
Set MItem = Nothing
End Sub

Since you did not state it is mandatory to use VBA (at least when this answer was first posted), you might:
Go to Home -> Find & Select -> Go To Special -> Visible cells only. Then copy and paste into your email. That worked for me.

Related

Send multiple emails with different deferred delivery times to one email address

I want to send email with deferred delivery according to the cells ("A2:A4").
For instance, if today is 2 February 2023, send three emails for delivery on 6 February, 13 February and 20 February.
The VBA code sends an email for last cell ("A4").
For ("A2") AND ("A3") the email won't be created.
Sub Send_Deferred_Mail_From_Excel()
Dim OutlookApp As Object
Dim OutlookMail As Object
Dim xRg As Range
Set xRg = Range("A2:A4")
Set OutlookApp = CreateObject("Outlook.Application")
Set OutlookMail = OutlookApp.CreateItem(0)
'Send Email Using Excel VBA Macro Code
With OutlookMail
.To = "email"
.CC = ""
.BCC = ""
.Subject = "HI"
.Body = "HELLO"
'Send email on specific day & time
.DeferredDeliveryTime = Range("A2") + Range("A3") + Range("A4")
.Display 'or just put .Send to directly send the mail instead of display
End With
Set OutlookMail = Nothing
Set OutlookApp = Nothing
End Sub
Please try it like this.
Make a list in Sheets("Sheet1") with :
In column A : Names of the people
In column B : E-mail addresses
In column C:Z : Filenames like this C:\Data\Book2.xls (don't have to be Excel files)
The Macro will loop through each row in "Sheet1" and if there is a E-mail address in column B
and file name(s) in column C:Z it will create a mail with this information and send it.
Sub Send_Files()
'Working in Excel 2000-2016
'For Tips see: http://www.rondebruin.nl/win/winmail/Outlook/tips.htm
Dim OutApp As Object
Dim OutMail As Object
Dim sh As Worksheet
Dim cell As Range
Dim FileCell As Range
Dim rng As Range
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set sh = Sheets("Sheet1")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In sh.Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'Enter the path/file names in the C:Z column in each row
Set rng = sh.Cells(cell.Row, 1).Range("C1:Z1")
If cell.Value Like "?*#?*.?*" And _
Application.WorksheetFunction.CountA(rng) > 0 Then
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = cell.Value
.Subject = "Testfile"
.Body = "Hi " & cell.Offset(0, -1).Value
For Each FileCell In rng.SpecialCells(xlCellTypeConstants)
If Trim(FileCell) <> "" Then
If Dir(FileCell.Value) <> "" Then
.Attachments.Add FileCell.Value
End If
End If
Next FileCell
.Send 'Or use .Display
End With
Set OutMail = Nothing
End If
Next cell
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Sub Send_Deferred_Mail_From_Excel()
Dim OutlookApp As Object
Dim OutlookMail As Object
Dim xRg As Range
Set xRg = Range("A2:A4")
Set OutlookApp = CreateObject("Outlook.Application")
Set OutlookMail = OutlookApp.CreateItem(0)
'Send Email Using Excel VBA Macro Code
With OutlookMail
.To = "email"
.CC = ""
.BCC = ""
.Subject = "HI"
.Body = "HELLO"
''Try in a loop instead.
for each cell in xRg
'Send email on specific day & time
.DeferredDeliveryTime = cell
.Display 'or just put .Send to directly send the mail instead of display
next cell
End With
Set OutlookMail = Nothing
Set OutlookApp = Nothing
End Sub
I've got how to send multiple emails to one email address using date & time in cells
Sub Send_Deferred_Mail_From_Excel()
Dim i As Integer, OutlookMail As Object, lr As Long
Dim xRg As Range, Bk As Range
' get last row
lr = Cells(Rows.Count, "A").End(xlUp).Row
' create range from row 2 to last row
Set xRg = Range("A2:A" & lr)
Set OutlookMail = CreateObject("Outlook.Application")
For Each Bk In xRg
With OutlookMail.CreateItem(0)
.To = "email#gmail.com"
.CC = ""
.subject = "HI"
.Body = "HELLO"
' Send email on specific day & time
.DeferredDeliveryTime = Bk.Value
.Display
End With
Next Bk
End Sub

Create one email with cell values from all relevant rows as the mail body

I have an Excel file with 1000+ rows. There is data in column A, addressee 1 email in column M, addressee 2 email in column N and validation column O.
Validation column mechanism: If the value in the cell is <0 then the row should be taken into the email.
I need a macro to draft one email and the content of email should be merged table of all rows that have negative value in column O (key).
The email should be adressed via Bcc to email addresses from column M and Cc to email addresses in column N.
The subject and content of the email is not that important, but I'd like to adjust it. For the purpose of the exercise it can be "Generic title", "Generic Content".
After the email is drafted, I need to click "send" in Outlook.
After sending an email for a row, the value in column O for this row should change to a green cell with "OK" value.
DUMMY DATA BEFORE MACRO RUN
THE MAIL
DUMMY DATA AFTER MACRO RUN
The code below creates a seperate email for each row instead of one email with cell values from column A combined in one table (or some other form) as the email body. How do I re-arrange it so it creates one email?
Sub Send_mails()
Dim OutApp As Object
Dim OutMail As Object
Dim cell as Range
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
For Each cell in Worksheets("test1"). Columns("O").Cells
Set OutMail = OutApp.CreateItem(0)
If cell.Value < 0 Then
With OutMail
.Bcc = Cells(cell.Row, "M").Value
.Cc = Cells(cell.Row, "N").Value
.Subject = "Gneric Subject"
.Body = "Generic body text, Values from column A for each row meeting the condition, to be put here"
.display
End With
Cells(cell.Row, "O").Value = "OK"
Set OutMail = Nothing
End If
Next Cell
End Sub
I am not really sure if I understood what you need!
From your dummy data I created a table and named it tbClient.
Here is the code and the mail output!
You need to set a Reference to Microsoft Word or you change the code to use Late Binding.
Option Explicit
Public ws As Worksheet
Public ol As ListObject
Public olRng As Range
Sub copyTabletoEmail()
Dim olCol As Integer
Application.ScreenUpdating = False
Set ws = Sheets("Test1")
Set ol = ws.ListObjects("tbClient")
' remove table filter buttons
If Not ol.ShowAutoFilterDropDown Then ol.ShowAutoFilterDropDown = True
' clear table filters
If ol.AutoFilter.FilterMode Then ol.AutoFilter.ShowAllData
' get validation column
olCol = ol.ListColumns("Validation").Index
' filter table
ol.Range.AutoFilter field:=olCol, Criteria1:="<0", Operator:=xlOr
' remove table filter buttons
ol.ShowAutoFilterDropDown = False
' select table to copy
Set olRng = ol.Range
' create mail
Call CreateMail
' Change values on 'Validation' column
ol.ListColumns(olCol).DataBodyRange.SpecialCells(xlCellTypeVisible).Value = "OK"
' clear table filters
If ol.AutoFilter.FilterMode Then ol.AutoFilter.ShowAllData
Application.ScreenUpdating = True
End Sub
Sub CreateMail()
Dim OutApp As Object
Dim OutMail As Object
Dim OutInsp As Object
Dim mailTo As String, mailCC As String
Dim olCol As Integer
Dim rCell As Range
Dim addRng As Range
On Error GoTo errHandler
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
' display mail
OutMail.display
' If you're sending the same message to everyone the next 2 lines can be moved outside of the loop
OutMail.Subject = "Generic Subject"
' Range of mail addresses
olCol = ol.ListColumns("Client mail").Index
Set addRng = ol.ListColumns(olCol).DataBodyRange.SpecialCells(xlCellTypeVisible)
' get the mail addresses
For Each rCell In addRng
With OutMail
mailTo = mailTo & rCell.Value & ";"
mailCC = mailCC & rCell.Offset(0, 1).Value & ";"
End With
Next rCell
OutMail.to = mailTo
OutMail.cc = mailCC
' Declare word Variables
Dim oWrdDoc As Word.Document
Dim oWdEditor As Word.Editors
' Get the Active Inspector
Set OutInsp = OutMail.GetInspector
' Get the document within the inspector
Set oWrdDoc = OutInsp.WordEditor
' Greetings
Dim bodyMessage As String
bodyMessage = "Hi Kapcer," & vbNewLine
oWrdDoc.Range.InsertBefore bodyMessage
' Paste the table
olRng.Copy
oWrdDoc.Range(Len(bodyMessage), Len(bodyMessage)).Paste
exitRoutine:
Application.CutCopyMode = False
' clear
Set OutMail = Nothing
Set OutApp = Nothing
Set ws = Nothing
Exit Sub
errHandler:
' Open immediate window to see the error
Debug.Print Err.Number, Err.Description
Resume exitRoutine
End Sub
If you are trying to send a single email, cc'd to all of the recipients in column N and bcc'd to all of the recipients in column M, then use logic like this:
Sub Send_mails()
Dim OutApp As Object
Dim OutMail As Object
Dim Recipient as Object
Dim ws as Worksheet
Dim cell as Range
Set ws = ThisWorkbook.Worksheets("test1") ' Assuming ThisWorkbook is correct
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
For Each cell in ws.Columns("O").Cells ' Better to limit this to the used range so you don't loop over 1 million rows.
If cell.Value < 0 Then
With OutMail
Set Recipient = .Recipients.Add(ws.Cells(cell.Row, "M").Value
Recipient.Type = olBcc
Set Recipient = .Recipients.Add(ws.Cells(cell.Row, "N").Value
Recipient.Type = olCc
' If you're sending the same message to everyone the next 2 lines can be moved outside of the loop
.Subject = "Gneric Subject"
.Body = "Generic body text, Values from column A for each row meeting the condition, to be put here"
End With
ws.Cells(cell.Row, "O").Value = "OK"
End If
Next Cell
OutMail.display
Set OutMail = Nothing
Set Recipient = Nothing
Set OutApp as Nothing
Set ws = Nothing
End Sub
I haven't run this, so there could be bugs.

Formatting email body from Excel using VBA

I have found the code below from here https://stackoverflow.com/a/49207287/4539709
Option Explicit
Public Sub Example()
' add ref - tool -> references - > Microsoft Outlook XX.X Object Library
Dim olApp As Outlook.Application
Set olApp = New Outlook.Application
Dim Email As Outlook.MailItem
Set Email = olApp.CreateItem(0)
' add ref - tool -> references - > Microsoft Word XX.X Object Library
Dim wdDoc As Word.Document '<=========
Set wdDoc = Email.GetInspector.WordEditor
Dim Sht As Excel.Worksheet
Set Sht = ThisWorkbook.Worksheets("Sheet1")
Dim rng As Range
Set rng = Sht.Range("A4:H16").SpecialCells(xlCellTypeVisible)
rng.Copy
With Email
.To = Sht.Range("C1")
.Subject = Sht.Range("B1")
.Display
wdDoc.Range.PasteAndFormat Type:=wdFormatOriginalFormatting
End With
End Sub
I have come across an issue with the code in that after you send an email the rows remain selected as per attached. Is there anyway to clear this
Add the following line at the end Application.CutCopyMode = False
With Email
.To = Sht.Range("C1")
.Subject = Sht.Range("B1")
.Display
wdDoc.Range.PasteAndFormat Type:=wdFormatOriginalFormatting
End With
Application.CutCopyMode = False '<---
End Sub

Paste text and multiple Excel ranges as images in Outlook using wordeditor

I am trying to paste multiple Excel ranges as images in Outlook mail using VBA. I am using the answer to this question (Pasting an Excel range into an email as a picture) to paste a range of excel as image in mail but as soon as I paste another range, it overwrites the previous image. Is there anyway to change the cursor position in Outlook mail using wordeditor. I tried using collapse before pasting the image but it did not help. Also how do I add the text to it as using Outmail.body to edit anything gets overwritten too by the image pasted afterwards.
This is the code I am using:
Sub Sendmail()
Dim r as range
Set r = Range("C2:O13)
r.copy
dim outlookapp as Outlook.Application
set outlookapp = CreateObject("Outlook.Application")
dim outMail As Outlook.Mailitem
Set outMail = outlookApp.CreateItem(olMailItem)
With outMail
.Display
.CC = "xyz#abc.com"
.Subject = "Test"
.Body = "Dear" & "Macro" & vbnewline
end with
outmail.Display
'Opening wordeditor
dim worddoc as Word.Document
Set worddoc = Outmail.GetInspector.WordEditor
worddoc.range.PasteandFormat wdChartPicture
'Adding new line after pasting image
worddoc.range.Insertafter vbNewline
' Adding second image
dim s as range
set s= Range(P2:Z30)
s.copy
worddoc.range.PasteandFormat wdChartPicture
You could refer to the below code:
Option Explicit
Public Sub Example()
Dim rng As Range
Dim olApp As Object
Dim Email As Object
Dim Sht As Excel.Worksheet
Dim wdDoc As Word.Document
Set Sht = ActiveWorkbook.Sheets("Dashboard")
Set rng = Sht.Range("B4:L17")
rng.CopyPicture Appearance:=xlScreen, Format:=xlPicture
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set olApp = CreateObject("Outlook.Application")
Set Email = olApp.CreateItem(0)
Set wdDoc = Email.GetInspector.WordEditor
With Email
.To = ""
.CC = ""
.BCC = ""
.Subject = ""
.Attachments.Add ActiveWorkbook.FullName
wdDoc.Range.PasteAndFormat Type:=wdChartPicture
' if need setup inlineshapes hight & width
With wdDoc
.InlineShapes(1).Height = 130
End With
.Display
End With
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set Email = Nothing
Set olApp = Nothing
End Sub
For more information, please refer to these links:
Copy Excel range as Picture to Outlook
Copy range of cells from Excel as picture and add text in the email body

Pasting an Excel range into an email as a picture

I'm creating an Outlook email from Excel (Office 2013). I want to paste a range of cells (C3:S52) into the email as a picture.
Below is the code I have so far. Where am I going wrong?
Sub Button193_Click()
'
' Button193_Click Macro
'
'
ActiveWindow.ScrollColumn = 2
ActiveWindow.ScrollColumn = 1
Range("C3:S52").Select
Selection.Copy
End Sub
Sub CreateMail()
Dim objOutlook As Object
Dim objMail As Object
Dim rngTo As Range
Dim rngSubject As Range
Dim rngBody As Range
Dim rngAttach As Range
Set objOutlook = CreateObject("Outlook.Application")
Set objMail = objOutlook.CreateItem(0)
With ActiveSheet
Set rngTo = .Range("E55")
Set rngSubject = .Range("E56")
Set rngBody = .Range("E57")
End With
With objMail
.To = rngTo.Value
.Subject = rngSubject.Value
.Body = rngBody.Value
.Display 'Instead of .Display, you can use .Send to send the email _
or .Save to save a copy in the drafts folder
End With
Set objOutlook = Nothing
Set objMail = Nothing
Set rngTo = Nothing
Set rngSubject = Nothing
Set rngBody = Nothing
Set rngAttach = Nothing
End Sub
Sub Button235_Click()
'
' Button235_Click Macro
'
'
ActiveWindow.ScrollColumn = 2
ActiveWindow.ScrollColumn = 1
Range("A1:M27").Select
Selection.Copy
End Sub
Sub RunThemAll()
Application.Run "Button193_Click"
Application.Run "CreateMail"
End Sub
Here's a worked example, tested in Office 2010:
'Copy range of interest
Dim r As Range
Set r = Range("B2:D5")
r.Copy
'Open a new mail item
Dim outlookApp As Outlook.Application
Set outlookApp = CreateObject("Outlook.Application")
Dim outMail As Outlook.MailItem
Set outMail = outlookApp.CreateItem(olMailItem)
'Get its Word editor
outMail.Display
Dim wordDoc As Word.Document
Set wordDoc = outMail.GetInspector.WordEditor
'To paste as picture
wordDoc.Range.PasteAndFormat wdChartPicture
'To paste as a table
'wordDoc.Range.PasteExcelTable LinkedToExcel:=False, WordFormatting:=False, RTF:=False
Result:
In the code above I used early binding to have access to autocomplete; to use this code you need to set references to the Microsoft Outlook and Microsoft Word object libraries: Tools > References... > set checkmarks like this:
Alternatively, you can forget about the references and use late binding, declaring all the Outlook and Word objects As Object instead of As Outlook.Application and As Word.Document etc.
Apparently you're having trouble implementing the above; the range pastes as a table rather than a picture in your email message. I have no explanation for why that would happen.
An alternative is then to paste as an image in Excel, and then cut and paste that image into your e-mail:
'Copy range of interest
Dim r As Range
Set r = Range("B2:D5")
r.Copy
'Paste as picture in sheet and cut immediately
Dim p As Picture
Set p = ActiveSheet.Pictures.Paste
p.Cut
'Open a new mail item
Dim outlookApp As Outlook.Application
Set outlookApp = CreateObject("Outlook.Application")
Dim outMail As Outlook.MailItem
Set outMail = outlookApp.CreateItem(olMailItem)
'Get its Word editor
outMail.Display
Dim wordDoc As Word.Document
Set wordDoc = outMail.GetInspector.WordEditor
'Paste picture
wordDoc.Range.Paste
As pointed out by WizzleWuzzle, there is also the option of using PasteSpecial instead of PasteAndFormat or Paste...
wordDoc.Range.PasteSpecial , , , , wdPasteBitmap
... but for some reason, the resulting image doesn't render as well. See how the lower table is kind of blurry:
I am providing an alternative solution to the above problem as Outlook.MailItem.GetInspector.WordEditor does not work in some organizational environments.
For security purposes, the HTMLBody, HTMLEditor, Body and WordEditor properties all are subject to address-information security prompts because the body of a message often contains the sender's or other people's e-mail addresses. And, if Group Policy does not permit then these prompts do not come on-screen. In simple words, as a developer, you are bound to change your code, because neither registry changes can be made nor group policy can be modified.
Hence, if your code suddenly stopped working after migrating to Office 365 or for any other reasons, please refer to the code below. Comments have been added for easy understanding and implementation.
If you have administrative rights then try the registry changes given at below link:
https://support.microsoft.com/en-au/help/926512/information-for-administrators-about-e-mail-security-settings-in-outlo
However, as developer, I recommend a code that's rather compatible with all versions of Excel instead of making system changes because system changes will be required on each end user's machine as well.
Code Compatible: Excel 2003, Excel 2007, Excel 2010, Excel 2013, Excel 2016, Office 365
Option Explicit
Sub Create_Email(ByVal strTo As String, ByVal strSubject As String)
Dim rngToPicture As Range
Dim outlookApp As Object
Dim Outmail As Object
Dim strTempFilePath As String
Dim strTempFileName As String
'Name it anything, doesn't matter
strTempFileName = "RangeAsPNG"
'rngToPicture is defined as NAMED RANGE in the workbook, do modify this name before use
Set rngToPicture = Range("rngToPicture")
Set outlookApp = CreateObject("Outlook.Application")
Set Outmail = outlookApp.CreateItem(olMailItem)
'Create an email
With Outmail
.To = strTo
.Subject = strSubject
'Create the range as a PNG file and store it in temp folder
Call createPNG(rngToPicture, strTempFileName)
'Embed the image in Outlook
strTempFilePath = Environ$("temp") & "\" & strTempFileName & ".png"
.Attachments.Add strTempFilePath, olByValue, 0
'Change the HTML below to add Header (Dear John) or signature (Kind Regards) using newline tag (<br />)
.HTMLBody = "<img src='cid:DashboardFile.png' style='border:0'>"
.Display
End With
Set Outmail = Nothing
Set outlookApp = Nothing
Set rngToPicture = Nothing
End Sub
Sub createPNG(ByRef rngToPicture As Range, nameFile As String)
Dim wksName As String
wksName = rngToPicture.Parent.Name
'Delete the existing PNG file of same name, if exists
On Error Resume Next
Kill Environ$("temp") & "\" & nameFile & ".png"
On Error GoTo 0
'Copy the range as picture
rngToPicture.CopyPicture
'Paste the picture in Chart area of same dimensions
With ThisWorkbook.Worksheets(wksName).ChartObjects.Add(rngToPicture.Left, rngToPicture.Top, rngToPicture.Width, rngToPicture.Height)
.Activate
.Chart.Paste
'Export the chart as PNG File to Temp folder
.Chart.Export Environ$("temp") & "\" & nameFile & ".png", "PNG"
End With
Worksheets(wksName).ChartObjects(Worksheets(wksName).ChartObjects.Count).Delete
End Sub

Resources