We have been using code that creates classical Win32 multiline tooltips in our legacy VB6 component for many years, since the times of Windows XP. It works fine in all latest versions of MS Windows (7, 8.1) except Windows 10. A parasitic horizontal gray line appears in the tooltip in this OS. The best demonstration of this problem is a tooltip window containing several lines of text (the main tip text is multiline and/or the tooltip has a bold title):
The correct tooltip should look like this (a screen from Windows 8.1):
Below is one more example of the same problem when the tooltip window does not have tile/icon but contains only multiline text:
This parasitic gray line is also present in a single-line tooltip - though it is not noticeable at first look:
What it could be? Is it a bug in Windows 10, or something has changed in the tooltip API?
Below is the code of the method used to initialize a tooltip:
Public Function Create(ByVal ParentHwnd As Long) As Boolean
Dim lWinStyle As Long
If m_lTTHwnd <> 0 Then
DestroyWindow m_lTTHwnd
End If
m_lParentHwnd = ParentHwnd
lWinStyle = TTS_ALWAYSTIP Or TTS_NOPREFIX
m_lTTHwnd = CreateWindowExA(0&, _
TOOLTIPS_CLASS, _
vbNullString, _
lWinStyle, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
0&, _
0&, _
App.hInstance, _
0&)
'now set our tooltip info structure
Dim tiA As TOOLINFOA
Dim tiW As TOOLINFOW
If g_bIsNt Then
With tiW
.lSize = Len(tiW)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.hWnd = m_lParentHwnd
.lId = m_lParentHwnd '0
.hInstance = App.hInstance
.lpStr = StrPtr(mvarTipText)
End With
Else
With tiA
.lSize = Len(tiA)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.hWnd = m_lParentHwnd
.lId = m_lParentHwnd
.hInstance = App.hInstance
.lpStr = mvarTipText
End With
End If
'add the tooltip structure
If g_bIsNt Then
SendMessage m_lTTHwnd, TTM_ADDTOOLW, 0&, tiW
Else
SendMessage m_lTTHwnd, TTM_ADDTOOLA, 0&, tiA
End If
'if we want a title or we want an icon
If mvarTitle <> vbNullString Or mvarIcon <> igToolTipIconNone Then
If g_bIsNt Then
SendMessage m_lTTHwnd, TTM_SETTITLEW, mvarIcon, ByVal StrPtr(mvarTitle)
Else
SendMessage m_lTTHwnd, TTM_SETTITLEA, mvarIcon, ByVal mvarTitle
End If
End If
' set the time parameters
SendMessageByLongA m_lTTHwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, mvarVisibleTime
SendMessageByLongA m_lTTHwnd, TTM_SETDELAYTIME, TTDT_INITIAL, mvarDelayTime
'according to MSDN, we should set TTM_SETMAXTIPWIDTH to a positive value
'to enable multiline tooltips
SendMessageByLongA m_lTTHwnd, TTM_SETMAXTIPWIDTH, 0, 100000
End Function
To solve the problem, we should not set the hwnd field of the TOOLINFO structure. The corresponding part of the code should look like this:
'now set our tooltip info structure
Dim tiA As TOOLINFOA
Dim tiW As TOOLINFOW
If g_bIsNt Then
With tiW
.lSize = Len(tiW)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.lId = m_lParentHwnd
.hInstance = App.hInstance
.lpStr = StrPtr(mvarTipText)
End With
Else
With tiA
.lSize = Len(tiA)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.lId = m_lParentHwnd
.hInstance = App.hInstance
.lpStr = mvarTipText
End With
End If
Related
I have this very simple code:
Private Sub Image87_Click()
PrintRTFWithMargins
End Sub
PrintRTFWithMargins is a function, which should "hopefully" print the contents of a RichTextBox. Every time I do run the code though, it gives me "Argument not optional" on PrintRTFWithMargins.
The code inside the function has already Option Explicit at the start, and I've tried to put it at the start of the Image87_Click too, but nothing.
Here's the code of PrintRTFWithMargins:
Option Explicit
Private Type Rect
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type CHARRANGE
cpMin As Long
cpMax As Long
End Type
Private Type FORMATRANGE
hdc As Long
hdcTarget As Long
rc As Rect
rcPage As Rect
chrg As CHARRANGE
End Type
Private Const WM_USER As Long = &H400
Private Const EM_FORMATRANGE As Long = WM_USER + 57
Private Const EM_SETTARGETDEVICE As Long = WM_USER + 72
Private Const PHYSICALOFFSETX As Long = 112
Private Const PHYSICALOFFSETY As Long = 113
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function SendMessage Lib "USER32" _
Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg As Long, _
ByVal wp As Long, lp As Any) As Long
Public Function PrintRTFWithMargins(RTFControl As Object, _
ByVal LeftMargin As Single, ByVal TopMargin As Single, _
ByVal RightMargin As Single, ByVal BottomMargin As Single) _
As Boolean
'********************************************************8
'PURPOSE: Prints Contents of RTF Control with Margins
'PARAMETERS:
'RTFControl: RichTextBox Control For Printing
'LeftMargin: Left Margin in Inches
'TopMargin: TopMargin in Inches
'RightMargin: RightMargin in Inches
'BottomMargin: BottomMargin in Inches
'***************************************************************
On Error GoTo ErrorHandler
'*************************************************************
'I DO THIS BECAUSE IT IS MY UNDERSTANDING THAT
'WHEN CALLING A SERVER DLL, YOU CAN RUN INTO
'PROBLEMS WHEN USING EARLY BINDING WHEN A PARAMETER
'IS A CONTROL OR A CUSTOM OBJECT. IF YOU JUST PLUG THIS INTO
'A FORM, YOU CAN DECLARE RTFCONTROL AS RICHTEXTBOX
'AND COMMENT OUT THE FOLLOWING LINE
If Not TypeOf RTFControl Is RichTextBox Then Exit Function
'**************************************************************
Dim lngLeftOffset As Long
Dim lngTopOffSet As Long
Dim lngLeftMargin As Long
Dim lngTopMargin As Long
Dim lngRightMargin As Long
Dim lngBottomMargin As Long
Dim typFr As FORMATRANGE
Dim rectPrintTarget As Rect
Dim rectPage As Rect
Dim lngTxtLen As Long
Dim lngPos As Long
Dim lngRet As Long
Dim iTempScaleMode As Integer
iTempScaleMode = Printer.ScaleMode
' needed to get a Printer.hDC
Printer.Print ""
Printer.ScaleMode = vbTwips
' Get the offsets to printable area in twips
lngLeftOffset = Printer.ScaleX(GetDeviceCaps(Printer.hdc, _
PHYSICALOFFSETX), vbPixels, vbTwips)
lngTopOffSet = Printer.ScaleY(GetDeviceCaps(Printer.hdc, _
PHYSICALOFFSETY), vbPixels, vbTwips)
' Get Margins in Twips
lngLeftMargin = InchesToTwips(LeftMargin) - lngLeftOffset
lngTopMargin = InchesToTwips(TopMargin) - lngTopOffSet
lngRightMargin = (Printer.Width - _
InchesToTwips(RightMargin)) - lngLeftOffset
lngBottomMargin = (Printer.Height - _
InchesToTwips(BottomMargin)) - lngTopOffSet
' Set printable area rect
rectPage.Left = 0
rectPage.Top = 0
rectPage.Right = Printer.ScaleWidth
rectPage.Bottom = Printer.ScaleHeight
' Set rect in which to print, based on margins passed in
rectPrintTarget.Left = lngLeftMargin
rectPrintTarget.Top = lngTopMargin
rectPrintTarget.Right = lngRightMargin
rectPrintTarget.Bottom = lngBottomMargin
' Set up the printer for this print job
typFr.hdc = Printer.hdc 'for rendering
typFr.hdcTarget = Printer.hdc 'for formatting
typFr.rc = rectPrintTarget
typFr.rcPage = rectPage
typFr.chrg.cpMin = 0
typFr.chrg.cpMax = -1
' Get length of text in the RichTextBox Control
lngTxtLen = Len(Form1.RichTextBox1.Text)
' print page by page
Do
' Print the page by sending EM_FORMATRANGE message
'Allows you to range of text within a specific device
'here, the device is the printer, which must be specified
'as hdc and hdcTarget of the FORMATRANGE structure
lngPos = SendMessage(Form1.RichTextBox1.hWnd, EM_FORMATRANGE, _
True, typFr)
If lngPos >= lngTxtLen Then Exit Do 'Done
typFr.chrg.cpMin = lngPos ' Starting position next page
Printer.NewPage ' go to next page
Printer.Print "" 'to get hDC again
typFr.hdc = Printer.hdc
typFr.hdcTarget = Printer.hdc
Loop
' Done
Printer.EndDoc
' This frees memory
lngRet = SendMessage(Form1.RichTextBox1.hWnd, EM_FORMATRANGE, _
False, Null)
Printer.ScaleMode = iTempScaleMode
PrintRTFWithMargins = True
Exit Function
ErrorHandler:
Err.Raise Err.Number, , Err.Description
End Function
Private Function InchesToTwips(ByVal Inches As Single) As Single
InchesToTwips = 1440 * Inches
End Function
I really, really don't know what else to put. It's such a simple code, just running a function, and yet "Argument not optional". It's single-hand the most annoying Visual Basic error I've ever experienced, because it's so dumb
'''
Call your function as:
Dim retVal as Boolean
retVal = PrintRTFWithMargins(RichTextBox1, 1.1, 1, 1, 1)
I'm essentially just trying to draw an icon image in a picture box.
I have the following subroutine. Input parameters verified and correct, however the icon does not display in the picture box when DrawIcon is called (this is part of a larger class).
Public Sub Draw_Icon(ByVal strDefaultIcon As String, ByVal lngIconNumber As Long, ByRef Picture_hDC As Long)
Dim lngIcon As Long
Dim lngError As Long
lngIcon = ExtractIcon(App.hInstance, strDefaultIcon, lngIconNumber)
If (lngIcon = 1 Or lngIcon = 0) Then
Call No_Icon(Picture_hDC)
Else
lngError = DrawIcon(Picture_hDC, 0, 0, lngIcon)
lngError = DestroyIcon(lngIcon)
End If
End Sub
Is there anything obvious I'm doing wrong? I've tried a number of solutions from StackOverflow and other sites to no avail.
Thank you very much for your answers. I fixed the issue with the following. I used a hidden, temporary image and picture box control to store the icon or image, respectively. Their contents are used to populate controls on the parent form. I hope that the code is readable. Thank you very much once again.
' Calling code
'
Public Function GetPictureOrIconAsImage(ByVal sFilename As String) As Picture
Dim strDefaultIcon As String
Dim lngIconNumber As Long
Dim Icon As New clsIcon
' Set error handler
On Error GoTo ErrorHandler
picTempPicture.Picture = LoadPicture("")
picTempIcon.Picture = LoadPicture("")
' Return picture if this is a picture file, otherwise attempt to return icon
If (modEasyQProcs.IsPictureFile(sFilename)) Then
picTempPicture.Picture = LoadPicture(sFilename)
Set GetPictureOrIconAsImage = picTempPicture.Picture
Else
If (Icon.GetDefaultIcon(sFilename, lngIconNumber, strDefaultIcon)) Then
Call Icon.Draw_Icon(strDefaultIcon, lngIconNumber, picTempIcon.hDC)
Else
Call Icon.No_Icon(picTempIcon.hDC)
End If
Set GetPictureOrIconAsImage = picTempIcon.Image
End If
Exit Function
ErrorHandler: ' Generic error handler
Call NonCriticalError(MODULE, Err, "GetPictureOrIconAsImage:ErrorHandler")
Err.Clear
' End of error handler scope
On Error GoTo 0
End Function
' Class Icon
'
Public Function GetDefaultIcon(ByRef FileName As String, ByRef lngIconNumber As Long, ByRef strDefaultIcon As String) As Boolean
'Parameters:
'FileName: The extension of the filename, with the "." e.g .doc
'Picture_hDC: The Handle to the device context of the Picture Box you want the icon
'to be displayed on.
'Example:
'Call GetDefaultIcon(".doc",Picture1.hDC)
Dim TempFileName As String
Dim lngError As Long
Dim lngRegKeyHandle As Long
Dim strProgramName As String
Dim lngStringLength As Long
Dim lngIcon As Long
Dim intN As Integer
GetDefaultIcon = False
TempFileName = Right(FileName, Len(FileName) - InStrRev(FileName, ".") + 1)
If (LCase(TempFileName) = ".exe") Then
strDefaultIcon = Space(260)
lngStringLength = GetSystemDirectory(strDefaultIcon, 260)
strDefaultIcon = Left(strDefaultIcon, lngStringLength) & "\SHELL32.DLL"
lngIconNumber = 2
GetDefaultIcon = True
Else
lngError = RegOpenKey(HKEY_CLASSES_ROOT, TempFileName, lngRegKeyHandle)
If (lngError = 0) Then
lngStringLength = 260
strProgramName = Space$(260)
lngError = RegQueryValueEx(lngRegKeyHandle, vbNullString, 0, 0, strProgramName, lngStringLength)
If (lngError = 0) Then
lngError = RegCloseKey(lngRegKeyHandle)
lngError = RegCloseKey(lngRegKeyHandle)
strProgramName = Left(strProgramName, lngStringLength - 1)
lngError = RegOpenKey(HKEY_CLASSES_ROOT, strProgramName & "\DefaultIcon", lngRegKeyHandle)
If (lngError = 0) Then
lngStringLength = 260
strDefaultIcon = Space$(260)
lngError = RegQueryValueEx(lngRegKeyHandle, vbNullString, 0, 0, strDefaultIcon, lngStringLength)
If (lngError) Then
lngError = RegCloseKey(lngRegKeyHandle)
Else
lngError = RegCloseKey(lngRegKeyHandle)
strDefaultIcon = Trim$(Left(strDefaultIcon, lngStringLength - 1))
intN = InStrRev(strDefaultIcon, ",")
If (intN >= 1) Then
lngIconNumber = Trim$(Right(strDefaultIcon, Len(strDefaultIcon) - intN))
strDefaultIcon = Trim$(Left(strDefaultIcon, intN - 1))
GetDefaultIcon = True
End If
End If
End If
End If
End If
End If
End Function
Public Sub Draw_Icon(ByVal strDefaultIcon As String, ByVal lngIconNumber As Long, ByRef Picture_hDC As Long)
Dim lngIcon As Long
Dim lngError As Long
lngIcon = ExtractIcon(App.hInstance, strDefaultIcon, lngIconNumber)
If (lngIcon = 1 Or lngIcon = 0) Then
Call No_Icon(Picture_hDC)
Else
lngError = DrawIcon(Picture_hDC, 0, 0, lngIcon)
If (lngError) Then lngError = DestroyIcon(lngIcon)
End If
End Sub
Public Sub No_Icon(ByRef Picture_hDC As Long)
Dim strDefaultIcon As String
Dim lngIconNumber As Long
Dim lngStringLength As Long
'No icon could be found so we use the normal windows icon
'This icon is held in shell32.dll in the system directory, Icon 0
strDefaultIcon = Space(260)
lngStringLength = GetSystemDirectory(strDefaultIcon, 260)
strDefaultIcon = Left(strDefaultIcon, lngStringLength) & "\SHELL32.DLL"
lngIconNumber = 0
Call Draw_Icon(strDefaultIcon, lngIconNumber, Picture_hDC)
End Sub
I built calculator in Access using a user form. The goal of the calculator is to document the steps taken by the user in solving a problem. It's similar to a high-school student being told to 'show their work'. I need to record a visual representation of the form. A PDF would be perfected, but I can't use PDFs.
I'm limited to file formats that are supported by our imaging server.
I know that the imaging server supports: tif, jpg, bmp and rtf. It might support other formats.
I know that these formats don't work: pdf, gif and png.
I'm an inexperienced coder (less than 6 mos), and I came up with a solution which I suspect is subpar. Occasionally, it seems to just stop working.
Essentially, I copy the form using keybd_event, and paste it into a word document, and save it as a tif file.
Is there a more conventional way of accomplishing this?
Here's my code:
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_KEYUP = &H2
Private Const VK_SNAPSHOT = &H2C
Private Const VK_MENU = &H12
Public Sub sendToFaf()
Dim appWord As New Word.Application
Dim docWord As New Word.Document
Dim imgWord As InlineShape
Dim thisForm As Form
Dim oldPrinter As String
Dim rnd As Integer
Dim strRnd As String
Dim oldWidth As Integer
Dim oldHeight As Integer
On Error GoTo ProcessError
DoCmd.Echo (False)
DoCmd.Hourglass (True)
Set appWord = CreateObject("word.application")
Set docWord = appWord.Documents.Add
Set thisForm = Screen.ActiveForm
appWord.Visible = False
appWord.DisplayAlerts = wdAlertsNone
oldWidth = thisForm.InsideWidth
oldHeight = thisForm.InsideHeight
thisForm.InsideWidth = 10800
thisForm.InsideHeight = 11925
keybd_event VK_MENU, 0, 0, 0
DoEvents
keybd_event VK_SNAPSHOT, 0, 0, 0
DoEvents
keybd_event VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0
keybd_event VK_MENU, 0, KEYEVENTF_KEYUP, 0
DoEvents
thisForm.InsideWidth = oldWidth
thisForm.InsideHeight = oldHeight
rnd = Int((10000 - 0 + 1) * Math.rnd + 0)
strRnd = Format(rnd, "0000")
With docWord.PageSetup
.TopMargin = 0
.BottomMargin = 0
.LeftMargin = 0
.RightMargin = 0
.VerticalAlignment = wdAlignVerticalCenter
End With
docWord.Paragraphs.Alignment = wdAlignParagraphCenter
appWord.Selection.Paste
Set imgWord = docWord.InlineShapes(docWord.InlineShapes.Count)
imgWord.Width = InchesToPoints(8.5)
oldPrinter = appWord.ActivePrinter
appWord.ActivePrinter = "FAX"
appWord.PrintOut _
Background:=False, _
outputfilename:="c:\a faf\" & thisForm.Name & strRnd & ".tif", _
PrintToFile:=True
MsgBox ("File created: 'c:\a faf\" & thisForm.Name & strRnd & ".tif'")
appWord.ActivePrinter = oldPrinter
ProcessExit:
Set imgWord = Nothing
docWord.Close savechanges:=wdDoNotSaveChanges
appWord.Quit savechanges:=wdDoNotSaveChanges
Set docWord = Nothing
Set appWord = Nothing
Set thisForm = Nothing
DoCmd.Echo (True)
DoCmd.Hourglass (False)
Exit Sub
ProcessError:
MsgBox Err.Number & vbCrLf & Err.Description & vbCrLf & "Sub SendToFaf"
GoTo ProcessExit
End Sub
We use classic native OS tooltips for clipped texts. However, if so-called large fonts are used in the OS
, our tooltip window appears as an empty window of a very small size (about 13x5 pixels). See it magnified below - it is near the cursor:
Is it a known bug? If so, how to solve this problem?
Below is the code of the method used to initialize a tooltip:
Public Function Create(ByVal ParentHwnd As Long) As Boolean
Dim lWinStyle As Long
If m_lTTHwnd <> 0 Then
DestroyWindow m_lTTHwnd
End If
m_lParentHwnd = ParentHwnd
lWinStyle = TTS_ALWAYSTIP Or TTS_NOPREFIX
m_lTTHwnd = CreateWindowExA(0&, _
TOOLTIPS_CLASS, _
vbNullString, _
lWinStyle, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
0&, _
0&, _
App.hInstance, _
0&)
'now set our tooltip info structure
Dim tiA As TOOLINFOA
Dim tiW As TOOLINFOW
If g_bIsNt Then
With tiW
.lSize = Len(tiW)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.hWnd = m_lParentHwnd
.lId = m_lParentHwnd '0
.hInstance = App.hInstance
.lpStr = StrPtr(mvarTipText)
End With
Else
With tiA
.lSize = Len(tiA)
.lFlags = TTF_SUBCLASS Or TTF_IDISHWND
.hWnd = m_lParentHwnd
.lId = m_lParentHwnd
.hInstance = App.hInstance
.lpStr = mvarTipText
End With
End If
'add the tooltip structure
If g_bIsNt Then
SendMessage m_lTTHwnd, TTM_ADDTOOLW, 0&, tiW
Else
SendMessage m_lTTHwnd, TTM_ADDTOOLA, 0&, tiA
End If
'if we want a title or we want an icon
If mvarTitle <> vbNullString Or mvarIcon <> igToolTipIconNone Then
If g_bIsNt Then
SendMessage m_lTTHwnd, TTM_SETTITLEW, mvarIcon, ByVal StrPtr(mvarTitle)
Else
SendMessage m_lTTHwnd, TTM_SETTITLEA, mvarIcon, ByVal mvarTitle
End If
End If
' set the time parameters
SendMessageByLongA m_lTTHwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, mvarVisibleTime
SendMessageByLongA m_lTTHwnd, TTM_SETDELAYTIME, TTDT_INITIAL, mvarDelayTime
'according to MSDN, we should set TTM_SETMAXTIPWIDTH to a positive value
'to enable multiline tooltips
SendMessageByLongA m_lTTHwnd, TTM_SETMAXTIPWIDTH, 0, 2147483647
End Function
Setting TTM_SETMAXTIPWIDTH to 100000 helped to solve the problem:
SendMessageByLongA m_lTTHwnd, TTM_SETMAXTIPWIDTH, 0, 100000
#JonathanPotter, thanks a million.
I have an Excel file which includes pictures in column B and I want like to export them into several files as .jpg (or any other picture file format). The name of the file should be generated from text in column A. I tried following VBA macro:
Private Sub CommandButton1_Click()
Dim oTxt As Object
For Each cell In Ark1.Range("A1:A" & Ark1.UsedRange.Rows.Count)
' you can change the sheet1 to your own choice
saveText = cell.Text
Open "H:\Webshop_Zpider\Strukturbildene\" & saveText & ".jpg" For Output As #1
Print #1, cell.Offset(0, 1).text
Close #1
Next cell
End Sub
The result is that it generates files (jpg), without any content. I assume the line Print #1, cell.Offset(0, 1).text. is wrong.
I don't know what I need to change it into, cell.Offset(0, 1).pix?
Can anybody help me? Thanks!
If i remember correctly, you need to use the "Shapes" property of your sheet.
Each Shape object has a TopLeftCell and BottomRightCell attributes that tell you the position of the image.
Here's a piece of code i used a while ago, roughly adapted to your needs. I don't remember the specifics about all those ChartObjects and whatnot, but here it is:
For Each oShape In ActiveSheet.Shapes
strImageName = ActiveSheet.Cells(oShape.TopLeftCell.Row, 1).Value
oShape.Select
'Picture format initialization
Selection.ShapeRange.PictureFormat.Contrast = 0.5: Selection.ShapeRange.PictureFormat.Brightness = 0.5: Selection.ShapeRange.PictureFormat.ColorType = msoPictureAutomatic: Selection.ShapeRange.PictureFormat.TransparentBackground = msoFalse: Selection.ShapeRange.Fill.Visible = msoFalse: Selection.ShapeRange.Line.Visible = msoFalse: Selection.ShapeRange.Rotation = 0#: Selection.ShapeRange.PictureFormat.CropLeft = 0#: Selection.ShapeRange.PictureFormat.CropRight = 0#: Selection.ShapeRange.PictureFormat.CropTop = 0#: Selection.ShapeRange.PictureFormat.CropBottom = 0#: Selection.ShapeRange.ScaleHeight 1#, msoTrue, msoScaleFromTopLeft: Selection.ShapeRange.ScaleWidth 1#, msoTrue, msoScaleFromTopLeft
'/Picture format initialization
Application.Selection.CopyPicture
Set oDia = ActiveSheet.ChartObjects.Add(0, 0, oShape.Width, oShape.Height)
Set oChartArea = oDia.Chart
oDia.Activate
With oChartArea
.ChartArea.Select
.Paste
.Export ("H:\Webshop_Zpider\Strukturbildene\" & strImageName & ".jpg")
End With
oDia.Delete 'oChartArea.Delete
Next
This code:
Option Explicit
Sub ExportMyPicture()
Dim MyChart As String, MyPicture As String
Dim PicWidth As Long, PicHeight As Long
Application.ScreenUpdating = False
On Error GoTo Finish
MyPicture = Selection.Name
With Selection
PicHeight = .ShapeRange.Height
PicWidth = .ShapeRange.Width
End With
Charts.Add
ActiveChart.Location Where:=xlLocationAsObject, Name:="Sheet1"
Selection.Border.LineStyle = 0
MyChart = Selection.Name & " " & Split(ActiveChart.Name, " ")(2)
With ActiveSheet
With .Shapes(MyChart)
.Width = PicWidth
.Height = PicHeight
End With
.Shapes(MyPicture).Copy
With ActiveChart
.ChartArea.Select
.Paste
End With
.ChartObjects(1).Chart.Export Filename:="MyPic.jpg", FilterName:="jpg"
.Shapes(MyChart).Cut
End With
Application.ScreenUpdating = True
Exit Sub
Finish:
MsgBox "You must select a picture"
End Sub
was copied directly from here, and works beautifully for the cases I tested.
''' Set Range you want to export to the folder
Workbooks("your workbook name").Sheets("yoursheet name").Select
Dim rgExp As Range: Set rgExp = Range("A1:H31")
''' Copy range as picture onto Clipboard
rgExp.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
''' Create an empty chart with exact size of range copied
With ActiveSheet.ChartObjects.Add(Left:=rgExp.Left, Top:=rgExp.Top, _
Width:=rgExp.Width, Height:=rgExp.Height)
.Name = "ChartVolumeMetricsDevEXPORT"
.Activate
End With
''' Paste into chart area, export to file, delete chart.
ActiveChart.Paste
ActiveSheet.ChartObjects("ChartVolumeMetricsDevEXPORT").Chart.Export "C:\ExportmyChart.jpg"
ActiveSheet.ChartObjects("ChartVolumeMetricsDevEXPORT").Delete
Dim filepath as string
Sheets("Sheet 1").ChartObjects("Chart 1").Chart.Export filepath & "Name.jpg"
Slimmed down the code to the absolute minimum if needed.
New versions of excel have made old answers obsolete. It took a long time to make this, but it does a pretty good job. Note that the maximum image size is limited and the aspect ratio is ever so slightly off, as I was not able to perfectly optimize the reshaping math. Note that I've named one of my worksheets wsTMP, you can replace it with Sheet1 or the like. Takes about 1 second to print the screenshot to target path.
Option Explicit
Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Sub weGucciFam()
Dim tmp As Variant, str As String, h As Double, w As Double
Application.PrintCommunication = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
If Application.StatusBar = False Then Application.StatusBar = "EVENTS DISABLED"
keybd_event vbKeyMenu, 0, 0, 0 'these do just active window
keybd_event vbKeySnapshot, 0, 0, 0
keybd_event vbKeySnapshot, 0, 2, 0
keybd_event vbKeyMenu, 0, 2, 0 'sendkeys alt+printscreen doesn't work
wsTMP.Paste
DoEvents
Const dw As Double = 1186.56
Const dh As Double = 755.28
str = "C:\Users\YOURUSERNAMEHERE\Desktop\Screenshot.jpeg"
w = wsTMP.Shapes(1).Width
h = wsTMP.Shapes(1).Height
Application.DisplayAlerts = False
Set tmp = Charts.Add
On Error Resume Next
With tmp
.PageSetup.PaperSize = xlPaper11x17
.PageSetup.TopMargin = IIf(w > dw, dh - dw * h / w, dh - h) + 28
.PageSetup.BottomMargin = 0
.PageSetup.RightMargin = IIf(h > dh, dw - dh * w / h, dw - w) + 36
.PageSetup.LeftMargin = 0
.PageSetup.HeaderMargin = 0
.PageSetup.FooterMargin = 0
.SeriesCollection(1).Delete
DoEvents
.Paste
DoEvents
.Export Filename:=str, Filtername:="jpeg"
.Delete
End With
On Error GoTo 0
Do Until wsTMP.Shapes.Count < 1
wsTMP.Shapes(1).Delete
Loop
Application.PrintCommunication = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.StatusBar = False
End Sub
Thanks for the ideas! I used the above ideas to make a macro to do a bulk file conversion--convert every file of one format in a folder to another format.
This code requires a sheet with cells named "FilePath" (which must end in a "\"), "StartExt" (original file extension), and "EndExt" (desired file extension). Warning: it doesn't ask for confirmation before replacing existing files with the same name and extension.
Private Sub CommandButton1_Click()
Dim path As String
Dim pathExt As String
Dim file As String
Dim oldExt As String
Dim newExt As String
Dim newFile As String
Dim shp As Picture
Dim chrt As ChartObject
Dim chrtArea As Chart
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Get settings entered by user
path = Range("FilePath")
oldExt = Range("StartExt")
pathExt = path & "*." & oldExt
newExt = Range("EndExt")
file = Dir(pathExt)
Do While Not file = "" 'cycle through all images in folder of selected format
Set shp = ActiveSheet.Pictures.Insert(path & file) 'Import image
newFile = Replace(file, "." & oldExt, "." & newExt) 'Determine new file name
Set chrt = ActiveSheet.ChartObjects.Add(0, 0, shp.Width, shp.Height) 'Create blank chart for embedding image
Set chrtArea = chrt.Chart
shp.CopyPicture 'Copy image to clipboard
With chrtArea 'Paste image to chart, then export
.ChartArea.Select
.Paste
.Export (path & newFile)
End With
chrt.Delete 'Delete chart
shp.Delete 'Delete imported image
file = Dir 'Advance to next file
Loop
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Here is another cool way to do it- using en external viewer that accepts command line switches (IrfanView in this case) :
* I based the loop on what Michal Krzych has written above.
Sub ExportPicturesToFiles()
Const saveSceenshotTo As String = "C:\temp\"
Const pictureFormat As String = ".jpg"
Dim pic As Shape
Dim sFileName As String
Dim i As Long
i = 1
For Each pic In ActiveSheet.Shapes
pic.Copy
sFileName = saveSceenshotTo & Range("A" & i).Text & pictureFormat
Call ExportPicWithIfran(sFileName)
i = i + 1
Next
End Sub
Public Sub ExportPicWithIfran(sSaveAsPath As String)
Const sIfranPath As String = "C:\Program Files\IrfanView\i_view32.exe"
Dim sRunIfran As String
sRunIfran = sIfranPath & " /clippaste /convert=" & _
sSaveAsPath & " /killmesoftly"
' Shell is no good here. If you have more than 1 pic, it will
' mess things up (pics will over run other pics, becuase Shell does
' not make vba wait for the script to finish).
' Shell sRunIfran, vbHide
' Correct way (it will now wait for the batch to finish):
call MyShell(sRunIfran )
End Sub
Edit:
Private Sub MyShell(strShell As String)
' based on:
' http://stackoverflow.com/questions/15951837/excel-vba-wait-for-shell-command-to-complete
' by Nate Hekman
Dim wsh As Object
Dim waitOnReturn As Boolean:
Dim windowStyle As VbAppWinStyle
Set wsh = VBA.CreateObject("WScript.Shell")
waitOnReturn = True
windowStyle = vbHide
wsh.Run strShell, windowStyle, waitOnReturn
End Sub