创建 Outlook 电子邮件,并将不同工作表中的 Excel 命名范围和 3 个图表作为图像添加到电子邮件正文

问题描述 投票:0回答:1

我正在尝试修改 @PeterT 的一些代码,它将图表作为图像添加到 Outlook 电子邮件中。他的代码非常适合将图表添加到电子邮件中,并且速度超级快(谢谢@PeterT!)

我正在尝试将 Excel 命名范围(命名范围的范围每个月都会变化)添加到电子邮件正文中,作为*图表之前的图像。到目前为止,我所做的尝试确实将图像捕获到剪贴板,但随后我必须手动将其粘贴到图表上方的电子邮件正文。

理想情况下,我想将范围图像保存到临时文件名,就像宏/函数对图表所做的那样,然后让宏立即将范围和图表插入电子邮件正文中。

谢谢, 唐

@PeterT 的代码稍作修改即可捕获 3 个特定图表与 ActiveSheet 上的所有图表:

Option Explicit

'https://stackoverflow.com/questions/34161736/sending-a-chart-in-mail-body
'Super fast at adding the Charts to an email

Sub CreateEmail()
        Const PR_ATTACH_MIME_TAG = "http://schemas.microsoft.com/mapi/proptag/0x370E001E"
        Const PR_ATTACH_CONTENT_ID = "http://schemas.microsoft.com/mapi/proptag/0x3712001E"
        Dim wb As Workbook
        Dim ws As Worksheet
        Dim olApp As Object
        Dim olMail As Object
        Dim msg As String
        Dim msgGreeting As String
        Dim msgPara1 As String
        Dim msgEnding As String
        Dim chrt As ChartObject
        Dim fname As String
        Dim ident As String
        Dim tempFiles As Collection
        Dim imgIdents As Collection
        Dim imgFile As Variant
        Dim attchmt As Object
        Dim oPa As Object
        Dim i As Integer
        Dim chartNumbers As Variant
        Dim ii As Long
        Dim myChartObj As ChartObject
        Dim rng As Range
        Dim imgFileName As String
        
        '--- create the email body with HTML-formatted content
        msgGreeting = "<bold>Team</bold>,<br><br>"
        msgPara1 = "<div>Some Text1</div><br>" & _
                    "<div><bold>Some Text2</bold><br>" & _
                    "(Some Text3)</div><br><br>"
        msgEnding = "<br><br>Thanks,<br>MyName<br>MyTitle<br>MyPhoneNumber<br>"
        
        '--- build the other email body content
        Set wb = ActiveWorkbook
        Set ws = ActiveSheet
        msg = msgGreeting & msgPara1
        
        '--- This captures a picture of the Range, but needs to be manually pasted to the email
        ' Set the range you want to copy
        Set rng = Worksheets("Daily Average").Range("DailyAverage")
        ' Copy the range as an image
        rng.CopyPicture Appearance:=xlScreen, Format:=xlPicture
        
         '--- loops and adds all charts found on the worksheet
        If ws.ChartObjects.Count > 0 Then
            Set tempFiles = New Collection
            Set imgIdents = New Collection
             
          '--- Use this section to loop thru all the Chart Objects on the ActiveSheet
          '    instead of using the array of specific Charts below
'            For Each chrt In ws.ChartObjects
'                fname = ""
'                msg = msg & ChartToEmbeddedHTML(chrt, fname, ident) & "<br><br>"
'                tempFiles.Add fname
'                imgIdents.Add ident
'            Next chrt

            ' Define the array of chart numbers you want to process
            'Added this section FROM HERE.....
            chartNumbers = Array(10, 15, 16)
        
            For ii = LBound(chartNumbers) To UBound(chartNumbers)
                Set myChartObj = ws.ChartObjects("Chart " & chartNumbers(ii))
            '--- This With section resizes the Charts on the ActiveSheet
'                With myChartObj
'                    .Height = 350
'                    .Width = 600
'                End With
                fname = ""
                msg = msg & ChartToEmbeddedHTML(myChartObj, fname, ident) & "<br><br>"
                tempFiles.Add fname
                imgIdents.Add ident
            Next ii
            '.....TO HERE and commented out the previous 6 lines to loop thru all the Chart Objects on the ActiveSheet
        
        End If
        msg = msg & msgEnding

        '--- create the mail item
        Set olApp = CreateObject("Outlook.Application")
        Set olMail = olApp.CreateItem(0)                'olMailItem=0
        With olMail
            .To = ThisWorkbook.Sheets("Signature").Range("D2").Value  '  "[email protected]"
            .CC = ThisWorkbook.Sheets("Signature").Range("D4").Value '   "xxxx@xxx"
            .Subject = ThisWorkbook.Sheets("Signature").Range("D7").Value & " - " & Format(Date, "dd-mmm-yyyy")  '   "xxxx"
            .BodyFormat = 2        'olFormatHTML=2

            '--- each of the images is referenced as a filename, but each one must be
            '    individually added as an attachment, then the attachment properties
            '    set to show the attachment as "inline". Because the image will be
            '    inlined, we'll use the "ident" as the reference (internal to the
            '    message body HTML)
            If (Not tempFiles Is Nothing) Then
                For i = 1 To tempFiles.Count
                    Set attchmt = .Attachments.Add(tempFiles.Item(i))
                    Set oPa = attchmt.PropertyAccessor
                    oPa.SetProperty PR_ATTACH_MIME_TAG, "image/png"
                    oPa.SetProperty PR_ATTACH_CONTENT_ID, imgIdents.Item(i)
                Next i
            End If
            '--- the email item needs to be saved first
            .Save
            '--- now add the message contents
            .HTMLBody = msg
'''--- Not finding the right place for this
''' Paste the image into the email body
'olMail.GetInspector.WordEditor.Range.Paste
            .Display
        End With
        '--- delete the temp files now
        For Each imgFile In tempFiles
            Kill imgFile
        Next imgFile
        '--- clean up and get out
        Set tempFiles = Nothing
        Set imgIdents = Nothing
        Set attchmt = Nothing
        Set rng = Nothing
        Set oPa = Nothing
        Set olMail = Nothing
        Set olApp = Nothing
        Set ws = Nothing
        Set wb = Nothing
    End Sub
    
    Private Function ChartToEmbeddedHTML(thisChart As ChartObject, _
                                 ByRef tmpFile As String, _
                                 ByRef ident As String) As String
        Dim html As String
        ident = RandomString(8)
        tmpFile = thisChart.Parent.Parent.Path & "\" & ident & ".png"
        
        thisChart.Activate
        thisChart.Chart.Export FileName:=tmpFile, Filtername:="png"
        html = "<img alt='Excel Chart' src='cid:" & ident & "'></img>"
        ChartToEmbeddedHTML = html
    End Function
    
    Private Function RandomString(strlen As Integer) As String
        Dim i As Integer, iTemp As Integer, bOK As Boolean, strTemp As String
        '48-57 = 0 To 9, 65-90 = A To Z, 97-122 = a To z
        'amend For other characters If required
        For i = 1 To strlen
            Do
                iTemp = Int((122 - 48 + 1) * Rnd + 48)
                Select Case iTemp
                Case 48 To 57, 65 To 90, 97 To 122: bOK = True
                Case Else: bOK = False
                End Select
            Loop Until bOK = True
            bOK = False
            strTemp = strTemp & Chr(iTemp)
        Next i
        RandomString = strTemp
    End Function

我尝试的是将此代码插入到宏内的各个位置,但没有任何运气......基本上它只是插入图表并“忽略”Range.Paste 行。

'''--- Not finding the right place for this
''' Paste the image into the email body
'olMail.GetInspector.WordEditor.Range.Paste
excel vba email charts range
1个回答
0
投票

这是使用不同方法添加图像的示例。这对我来说比使用“cid+attachment”方法更干净。

Option Explicit

Sub Tester()
    
    Dim sht As Worksheet, html As String, olApp As Object, olMail As Object
    
    Set sht = ThisWorkbook.Worksheets("Sheet2") 'for example

    html = "<p>Header here</p>"
    html = html & ToImageTag(sht.Range("B2:H8")) & "<br>"
    html = html & "<p>Footer here</p>"
    html = html & ToImageTag(sht.ChartObjects("chart1").Chart) & "<br>"
    html = html & "<p>Footer2 here</p>"
    html = html & ToImageTag(sht.Range("C18:F31")) & "<br>"
    html = html & "<p>Footer3 here</p>"
    
    Set olApp = CreateObject("Outlook.Application")
    Set olMail = olApp.CreateItem(0) 'olMailItem=0
    With olMail
        .BodyFormat = 2              'olFormatHTML=2
        .HTMLBody = html
        .display
    End With
    
End Sub

'Convert a range or chart an <img> tag for HTML,with image content encoded as B64
Function ToImageTag(obj As Object) As String
    Dim b64 As String
    If TypeName(obj) = "Range" Then
        b64 = RangeToB64(obj)
    Else 'is a chart
        b64 = ChartToB64(obj)
    End If
    ToImageTag = "<img src='data:image/png;base64," & b64 & "'>"
End Function

'Convert a Range to a B64 string (via exported chart image)
Function RangeToB64(rng As Range) As String
    Dim cob, sc, i As Long
    
    rng.CopyPicture Appearance:=xlScreen, Format:=xlPicture
    Set cob = rng.Parent.ChartObjects.Add(100, 100, 200, 200)
    Set sc = cob.Chart.SeriesCollection 'remove any series which may have been auto-added...
    Do While sc.Count > 0
        sc(1).Delete
    Loop
    With cob
        .ShapeRange.line.Visible = msoFalse  '<<< remove chart border
        .Height = rng.Height
        .Width = rng.Width
        Do
            .Chart.Paste
            DoEvents
            i = i + 1
        Loop While i < 5 And .Chart.Shapes.Count = 0 'in case paste failed try 5 times...
        RangeToB64 = ChartToB64(.Chart)
        .Delete
    End With
End Function

'Convert chart to b64 string via exported temp file
Function ChartToB64(cht As Chart)
    Dim sPath As String
    sPath = TempPath
    cht.Export FileName:=sPath, Filtername:="PNG"
    ChartToB64 = EncodeBase64(sPath)
    Kill sPath
End Function

'get a B64-encoded string from a file
Function EncodeBase64(filePath As String) As String
    Dim bytes, b64
    With CreateObject("ADODB.Stream")
        .Open
        .Type = 1 'ADODB.adTypeBinary
        .LoadFromFile filePath
        bytes = .Read
        .Close
    End With
    With CreateObject("MSXML2.DOMDocument").createElement("b64")
        .DataType = "bin.base64"
        .nodeTypedValue = bytes
        EncodeBase64 = Replace(.text, vbLf, "")
    End With
End Function

'Return a temporary file path
Function TempPath() As String
    With CreateObject("scripting.filesystemobject")
        TempPath = .GetSpecialFolder(2) & "\tmp" & CLng(Rnd() * 1000000000#)
    End With
End Function
© www.soinside.com 2019 - 2024. All rights reserved.