excelvbaexcel-formula

Can I guarantee the Image Formula Function is finished before printing the result?


I would like to automatically print a QR code from one of my cells. The best way to do this that I've found is using the IMAGE function.

The function looks like this: =IMAGE("https://api.qrserver.com/v1/create-qr-code/?data="&ENCODEURL(B2))

Unfortunately, sometimes when I print after changing the value of my cell it prints before the QR Code is generated and I get something like this: The PrintPreview shows a text string of "Picture" where the QR code should be

I have tried several methods to get the function to finish, but none have been reliable.

Sub ChangePrintedQR()
    Dim ImgRange As Range
    Set ImgRange = Range("B4")
    Range("B2") = Now
'    ImgRange.Formula = "=IMAGE(""https://api.qrserver.com/v1/create-qr-code/?data=""&ENCODEURL(B2))" 'Having this line can print "Busy" instead of "Picture"

'    While ImgRange.Text = "Picture"    'This gets stuck in an infinite loop
'        Debug.Print ImgRange.Text      'regardless of this line, trying the Value of the cell does the same
'    Wend

'    Application.Calculate              'This method does nothing
'    If Not Application.CalculationState = xlDone Then
'        DoEvents
'    End If

'    Application.Wait (Now + TimeValue("0:00:10"))   'This results in the waiting, but no change to the printed sheet.

    ActiveSheet.PrintPreview
End Sub

Please note that I sometimes have gotten an updated Image when printing, but not other times, I would like to be able to print an updated Image every time.


Solution

  • Okay… After some testing, this appears to be one of the times when DoEvents is insufficient, and you need to allow code to fully end. To make this work, you can use the OnTime function, and split the function in two:

    Option Explicit
    Private imgRange AS Range
    
    Sub ChangePrintedQR()
        Set ImgRange = Range("B4")
        
        ImgRange.Formula = "=IMAGE(""https://api.qrserver.com/v1/create-qr-code/?data=""&ENCODEURL(B2))"
        ImgRange.Calculate
        
        Application.OnTime Now(), "PreviewPrintedQR"
    End Sub
    
    Sub PreviewPrintedQR()
        If ImgRange.Value = CVErr(2051) Then
            Application.OnTime Now() + TimeSerial(0, 0, 1), "PreviewPrintedQR"
        Else
            Set ImgRange = Nothing
            ActiveSheet.PrintPreview
        End If
    End Sub
    

    This will add one-second delays, and fully halt the VBA between loops — this allows the image to be downloaded and change the cell from #BUSY to #VALUE.

    (There may be a way to force/allow the connection/download to occur during VBA and turn this back into a single function, however I had no success with Refreshing Connections, Refreshing Links, DoEvents, etc)