VBA only running in debug mode - vba

What I'm trying to do is to copy a bunch of cells as a picture from one sheet and paste it in a chart object in another sheet. The following is the code used and it is running fine when used in debug mode, but I do no see the image being pasted in the chart when I run it normally.
Sub copy_paste_KDT()
'
' copy_paste_KDT Macro
'
'
Worksheets("KDT").Range("J12:AB37").CopyPicture Appearance:=xlScreen, Format:=xlPicture
Dim wb_path As String
wb_path = Application.ThisWorkbook.Path
'Dim objCht As ChartObject
'Set objCht = ActiveSheet.ChartObjects("KDT Rectangle")
'If Not objCht Is Nothing Then
If ActiveSheet.ChartObjects.Count > 0 Then
ActiveSheet.ChartObjects("KDT Rectangle").Delete
End If
With Worksheets("profile")
'Creating the Chart
.ChartObjects.Add(690, 125, 550, 245).Name = "KDT Rectangle"
End With
If Range("B11").Value = 0 Then
With Worksheets("profile")
Application.ScreenUpdating = True
'Application.Wait (Now + TimeValue("00:00:01"))
With .ChartObjects("KDT Rectangle")
.Chart.Paste
End With
End With
End If
End Sub
I have also tried few thing like waiting for 1 to 10 seconds before the image is being pasted but of no use. Even tried putting a loop to count from 1 to a billion, no use again. Finally wanted to check if the image is getting pasted in a random cell of the sheet and that works, but not in the chart object.
I would appreciate if someone could help me figure out why the image is not getting paste.
TL,DR: Macro to copy paste a part of excel as a screenshot into a chart creates a chart successfully but not able to populate the image when run (F5), but works perfectly in debug mode (F8).

Although I am using Excel 2010 and your code works fine in testing for me.
You can try putting a Select in before the .Chart.Paste this might help with pasting inside the chart. See code below, just added the line into your original code, so you were almost there.
Option Explicit
Sub copy_paste_KDT()
'
' copy_paste_KDT Macro
'
'
Worksheets("KDT").Range("J12:AB37").CopyPicture Appearance:=xlScreen, Format:=xlPicture
Dim wb_path As String
wb_path = Application.ThisWorkbook.Path
'Dim objCht As ChartObject
'Set objCht = ActiveSheet.ChartObjects("KDT Rectangle")
'If Not objCht Is Nothing Then
If ActiveSheet.ChartObjects.Count > 0 Then
ActiveSheet.ChartObjects("KDT Rectangle").Delete
End If
With Worksheets("profile")
'Creating the Chart
.ChartObjects.Add(690, 125, 550, 245).Name = "KDT Rectangle"
End With
If Range("B11").Value = 0 Then
With Worksheets("profile")
Application.ScreenUpdating = True
'Application.Wait (Now + TimeValue("00:00:01"))
With .ChartObjects("KDT Rectangle")
.Select 'Just added this
.Chart.Paste
End With
End With
End If
End Sub

Related

I'm attempting to generate barcodes and save them to a spreadsheet

I am trying to come up with a better way to do the following code. it works as is but due to the issues with windows clipboard memory leaks it's not reliable and not very fast. If possible I want to assign the image being copied from word.application directly into an array or at least be able to bypass the clipboard been trying for days.
Dim ShapeName As String
Const BarcodeWidth As Integer = 175
Dim ws As Worksheet, WdApp
Set ws = ActiveSheet
Set WdApp = CreateObject("Word.Application")
Do Until ActiveSheet.Cells(RowLoc, 1) = "End of File"
ShapeName = ActiveSheet.Cells(RowLoc, 1)
With WdApp.Documents.Add
.PageSetup.RightMargin = .PageSetup.PageWidth - .PageSetup.LeftMargin - BarcodeWidth
.Fields.Add(Range:=.Range, Type:=-1, Text:="DISPLAYBARCODE " & ShapeName & " CODE128 \d \t", PreserveFormatting:=False).Copy
End With
Sheets("Barcode").Cells(RowLoc, 5).Select 'selects the location where the bar code will be pasted
ws.PasteSpecial Format:="Picture (Enhanced Metafile)", Link:=False, DisplayAsIcon:=False 'Pastes the bar code at the current selection
RowLoc = RowLoc + 1
Selection.name = ShapeName
Application.CutCopyMode = False
Loop
WdApp.Quit SaveChanges:=False
Set WdApp = Nothing
End Sub
I never found a way to store the images in excel however I figured out the best way achieve what I was going for is to create code that preps the data in the format I need then mail merge the result into a template creates the shipping labels I am going for.

Excel to PowerPoint Copy/Paste Macro Inconsistently Fails

I am having a problem with a macro that copies named ranges and charts out of excel and into powerpoint. The macro functions as intended on my computer, however when when I run the macro on a coworker's computer I get Run-time error '-2147023170 (800706be)'. The problematic loop is below.
'Create an Instance of PowerPoint
On Error Resume Next
'Is PowerPoint already opened?
Set PowerPointApp = GetObject(class:="PowerPoint.Application")
'Clear the error between errors
Err.Clear
'If PowerPoint is not already open then open PowerPoint
If PowerPointApp Is Nothing Then
Set PowerPointApp = CreateObject(class:="PowerPoint.Application")
End If
'Handle if the PowerPoint Application is not found
If Err.Number = 429 Then
MsgBox "PowerPoint could not be found, aborting."
Exit Sub
End If
On Error GoTo 0
Application.ScreenUpdating = False
'Create a New Presentation
Set myPresentation = PowerPointApp.Presentations.Add
'Change aspect ratio
myPresentation.PageSetup.SlideSize = 2
cycle=1
For Each ch In ThisWorkbook.Sheets("Meeting Metrics").ChartObjects
'Add a slide to the Presentation
Set mySlide = myPresentation.Slides.Add(cycle, 11) '11 = ppLayoutTitleOnly
mySlide.Select
With mySlide.Shapes.Title.TextFrame.TextRange
.Text = slideTitles(cycle - 1)
With .Font
.Name = "Arial"
.Size = 32
.Color.RGB = RGB(237, 125, 49)
End With
End With
'Check if there is a table (Excel cell range) to copy for this slide
If Not IsMissing(copyRange(cycle - 1)) Then
'Copy Excel Range
ThisWorkbook.Sheets("Meeting Metrics").Range(copyRange(cycle - 1)).Copy
'Paste to PowerPoint
mySlide.Select
mySlide.Shapes.Paste
Set myShape = mySlide.Shapes(mySlide.Shapes.count)
Application.CutCopyMode = False
'Set position
myShape.Top = tableVertPos(cycle - 1) * 72
myShape.Left = tableHorPos(cycle - 1) * 72
End If
'Copy excel chart
ch.Select
ch.Chart.ChartArea.Copy
'Paste to PowerPoint
mySlide.Select
mySlide.Shapes.Paste
Set myShape = mySlide.Shapes(mySlide.Shapes.count)
Application.CutCopyMode = False
'Set position
myShape.Top = chartVertPos(cycle - 1) * 72
myShape.Left = chartHorPos(cycle - 1) * 72
cycle = cycle + 1
Next
When the error occurs, powerpoint will become unresponsive and ask to close. The error message will pop up, and debug will take me to one of lines containing mySlide (Not always the same line). If I try to hit the continue button, it results in Runtime error 462 since the powerpoint is closed. When I try to step through the program on the other computer to find the problematic line, it will step through a couple lines and then run like normal until it bugs out. However, if a throw a line break somewhere inside the loop and manually iterate it or put a message box inside the loop, the code will run fine.
I've tried inserting wait or sleep into the loop to see if this helps, but this just causes the code to halt for a few seconds before failing.
There appears to be a bug in the system described as a 'run ahead' in some places. I've had some success with the following ugly workaround...
Create the following UglyWait subprocedure
Sub UglyWait(Optional sec As Integer = 1)
Dim future As Date
future = DateAdd("s", sec, Now())
Do While Now() < future
DoEvents
Loop
End Sub
Place a call to UglyWait before and after any Powerpoint goto, paste, or save operations.
Application.Wait doesn't work, nor does a standard single DoEvents. This does seem to help though.

Excel/VBA Macros assistance

I am having a bit of trouble with some code and was wondering if someone could maybe assist. Basically I have 2 errors which I can't work out myself (too inexperienced with VBA, unfortunately)
Brief overview:
This macro is designed to generate a new workbook with copies of selected sheets from a "source" workbook in order to present to clients as a report batch. Essentially - we have master workbook "A" which may have 50 tabs or so, and we want to quickly select a couple of sheets to "copy" into a new workbook to save and send to a client. The code is a bit of a mess but I am not really sure what is going on/what I can remove etc.
Problems:
When you run the attached code/macro in Excel, it does everything it is supposed to do, however, it ALSO copies the sheet from which you run the macro. (i.e. I might be on sheet 1 in the Workbook. Run the macro to generate reports, checkbox menu appears and I select sheets 2, 5 & 9 - it will then copy into a new Workbook sheets 2, 5 & 9 AND sheet 1. But I never selected sheet 1 from the checkbox menu...)
Once this code has finished running, I am unable to save the Excel file. It just crashes and says "Microsoft Excel has stopped working" and then the file dies and I have to close Excel and recover etc. etc. I combined 2 pieces of code to get this working and I imagine I may be missing something crucial which is causing the problem. We have another piece of code to print sheets out in a similar way to this, and if I run this I am able to save with no problems.
Code:
I have included all the Visual Basic code (i.e. for the generate reports & print sheets macros).
I really don't have any experience with VBA so I hope someone will be able to assist! Thanks in advance :)
Sub PrintSelectedSheets()
Dim i As Integer
Dim TopPos As Integer
Dim SheetCount As Integer
Dim Printdlg As DialogSheet
Dim CurrentSheet As Worksheet, wsStartSheet As Worksheet
Dim CB As CheckBox
Application.ScreenUpdating = False
'Check for protected workbook
If ActiveWorkbook.ProtectStructure Then
MsgBox "Workbook is protected.", vbCritical
Exit Sub
End If
'Add a temporary dialog sheet
Set CurrentSheet = ActiveSheet
Set wsStartSheet = ActiveSheet
Set Printdlg = ActiveWorkbook.DialogSheets.Add
SheetCount = 0
'Add the checkboxes
TopPos = 40
For i = 1 To ActiveWorkbook.Worksheets.Count
Set CurrentSheet = ActiveWorkbook.Worksheets(i)
'Skip empty sheets and hidden sheets
If Application.CountA(CurrentSheet.Cells) <> 0 And _
CurrentSheet.Visible Then
SheetCount = SheetCount + 1
Printdlg.CheckBoxes.Add 78, TopPos, 150, 16.5
Printdlg.CheckBoxes(SheetCount).Text = _
CurrentSheet.Name
TopPos = TopPos + 13
End If
Next i
'Move the OK and Cancel buttons
Printdlg.Buttons.Left = 240
'Set dialog height, width, and caption
With Printdlg.DialogFrame
.Height = Application.Max _
(68, Printdlg.DialogFrame.Top + TopPos - 34)
.Width = 230
.Caption = "Select sheets to print"
End With
'Change tab order of OK and Cancel buttons
'so the 1st option button will have the focus
Printdlg.Buttons("Button 2").BringToFront
Printdlg.Buttons("Button 3").BringToFront
'Display the dialog box
CurrentSheet.Activate
wsStartSheet.Activate
Application.ScreenUpdating = True
If SheetCount <> 0 Then
'the following code will print the selected sheets as multiple print jobs.
'continuous page numbers will therefore not be printed
If Printdlg.Show Then
For Each CB In Printdlg.CheckBoxes
If CB.Value = xlOn Then
Worksheets(CB.Caption).Activate
ActiveSheet.PrintOut
'ActiveSheet.PrintPreview 'for debugging
End If
Next CB
'the following code will print the selected sheets as a single print job.
'This will allow the sheets to be printed with continuous page numbers.
'If Printdlg.Show Then
'For Each CB In Printdlg.CheckBoxes
'If CB.Value = xlOn Then
'Worksheets(CB.Caption).Select Replace:=False
'End If
'Next CB
'ActiveWindow.SelectedSheets.PrintOut copies:=1
'ActiveSheet.Select
Else
MsgBox "No worksheets selected"
End If
'End If
End If
'Delete temporary dialog sheet (without a warning)
Application.DisplayAlerts = False
Printdlg.Delete
'Reactivate original sheet
CurrentSheet.Activate
wsStartSheet.Activate
End Sub
Sub GenerateClientExcelReports()
'1. Declare variables
Dim i As Integer
Dim SheetCount As Integer
Dim TopPos As Integer
Dim lngCheckBoxes As Long, y As Long
Dim intTopPos As Integer, intSheetCount As Integer
Dim intHor As Integer 'this will be for the horizontal position of the items
Dim intWidth As Integer 'this will be for the overall width of the dialog box
Dim intLBLeft As Integer, intLBTop As Integer, intLBHeight As Integer
Dim Printdlg As DialogSheet
Dim CurrentSheet As Worksheet, wsStartSheet As Worksheet
Dim CB As CheckBox
'Dim wb As Workbook
'Dim wbNew As Workbook
'Set wb = ThisWorkbook
'Workbooks.Add ' Open a new workbook
'Set wbNew = ActiveWorkbook
On Error Resume Next
Application.ScreenUpdating = False
'2. Check for protected workbook
If ActiveWorkbook.ProtectStructure Then
MsgBox "Workbook is protected.", vbCritical
Exit Sub
End If
'3. Add a temporary dialog sheet
Set CurrentSheet = ActiveSheet
Set wsStartSheet = ActiveSheet
Set Printdlg = ActiveWorkbook.DialogSheets.Add
SheetCount = 0
'4. Add the checkboxes
TopPos = 40
For i = 1 To ActiveWorkbook.Worksheets.Count
Set CurrentSheet = ActiveWorkbook.Worksheets(i)
'5. Skip empty sheets and hidden sheets
If Application.CountA(CurrentSheet.Cells) <> 0 And _
CurrentSheet.Visible Then
SheetCount = SheetCount + 1
Printdlg.CheckBoxes.Add 78, TopPos, 150, 16.5
Printdlg.CheckBoxes(SheetCount).Text = _
CurrentSheet.Name
TopPos = TopPos + 13
End If
Next i
'6. Move the OK and Cancel buttons
Printdlg.Buttons.Left = 240
'7. Set dialog height, width, and caption
With Printdlg.DialogFrame
.Height = Application.Max _
(68, Printdlg.DialogFrame.Top + TopPos - 34)
.Width = 230
.Caption = "Select sheets to generate"
End With
'8. Change tab order of OK and Cancel buttons
' so the 1st option button will have the focus
Printdlg.Buttons("Button 2").BringToFront
Printdlg.Buttons("Button 3").BringToFront
'9. Display the dialog box
CurrentSheet.Activate
wsStartSheet.Activate
Application.ScreenUpdating = True
If SheetCount <> 0 Then
If Printdlg.Show Then
For Each CB In Printdlg.CheckBoxes
If CB.Value = xlOn Then
Worksheets(CB.Caption).Select Replace:=False
'For y = 1 To ActiveWorkbook.Worksheets.Count
'If WorksheetFunction.IsNumber _
'(InStr(1, "ActiveWorkbook.Sheets(y)", "Contents")) = True Then
'CB.y = xlOn
'End If
End If
Next
ActiveWindow.SelectedSheets.Copy
Else
MsgBox "No worksheets selected"
End If
End If
'Delete temporary dialog sheet (without a warning)
'Application.DisplayAlerts = False
'Printdlg.Delete
'Reactivate original sheet
'CurrentSheet.Activate
'wsStartSheet.Activate
'10. Delete temporary dialog sheet (without a warning)
Application.DisplayAlerts = False
Printdlg.Delete
'11. Reactivate original sheet
CurrentSheet.Activate
wsStartSheet.Activate
Application.DisplayAlerts = True
End Sub
Sub SelectAllCheckBox()
Dim CB As CheckBox
For Each CB In ActiveSheet.CheckBoxes
If CB.Name <> ActiveSheet.CheckBoxes(1).Text Then
CB.Value = ActiveSheet.CheckBoxes(1).Value
End If
Next CB
'ActiveSheet.CheckBoxes("Check Box 1").Value
End Sub
as for problem n°1
add a declaration of a boolean variable
Dim firstSelected As Boolean
and then modify the For Each CB In Printdlg.CheckBoxes loop block code as follows
If CB.Value = xlOn Then
If firstSelected Then
Worksheets(CB.Caption).Select Replace:=False
Else
Worksheets(CB.Caption).Select
firstSelected = True
End If
'For y = 1 To ActiveWorkbook.Worksheets.Count
'If WorksheetFunction.IsNumber _
'(InStr(1, "ActiveWorkbook.Sheets(y)", "Contents")) = True Then
'CB.y = xlOn
'End If
End If
since there's always an ActiveWorksheet when macro starts and thus if you only use Worksheets(CB.Caption).Select Replace:=False statement you keep adding it to the via Printdlg selected sheets.

vba Direct copy for buttons

I know that I could do something like..
range("C1:D1").copy destination:=range("C2:D2")
for ranges, I would like to know if I can do the same for form control buttons
Current code below copies the button if found and then adds the button to the cell where the "hash tag" was written. In this example "#Button Back To Summary#". This all works fine but I would like to change the code to not go via the clipboard, for example like the above code for a range but for a form button.
Calling Code:
On Error Resume Next
Cells.Find(What:="#Button Back To Summary#", After:=ActiveCell, LookIn:= _
xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
If Err.Number = 0 Then
addshapetocell ActiveCell, "BK_TO_SUMMARY"
End
DoEvents
On Error GoTo 0
addshapetocell()
Sub addshapetocell(p As Range, btn_Name As String)
Dim clLeft As Double
Dim clTop As Double
Dim cl As Range
Dim r As Integer, R1 As Integer
On Error GoTo 0
R1 = 0
r = 0
Set cl = Range(p.Address)
clLeft = cl.Left
clTop = cl.Top
cl.value = ""
retryer:
update_Working_Status
Application.CutCopyMode = False
DoEvents
If r > 5000 Or R1 > 700 Then
MsgBox "Code has attempted to copy a button 5000 times and has failed each time"
Stop
End If
Worksheets("Odds").Shapes(btn_Name).Copy
DoEvents
If Application.ClipboardFormats(1) = 0 Then
R1 = R1 + 1
Sleep (50)
GoTo retryer
End If
With ActiveSheet
On Error Resume Next
.Paste
If Err.Number = 1004 Then
On Error GoTo 0
r = r + 1
Sleep (50)
GoTo retryer
ElseIf Err.Number <> 0 Then
Stop 'unhandled error has happend
End If
On Error GoTo 0
.Shapes(btn_Name).Left = clLeft
.Shapes(btn_Name).Top = clTop
End With
End Sub
Edit: update_Working_Status updates the status bar with "Working." & "Working.." etc
I don't believe there is a way to directly copy the Shape from one Worksheet to another without using the Clipboard. There is a .Duplicate method but I'm not aware of a way to change the Shapes Parent ie. which Worksheet it belongs to.
Have you considered programmatically re-creating the Shape using your template Shape as a base? This would be, effectively, copying the Shape but with a bit more effort involved. I've written the following as an example of how you could do this which, hopefully, you can adapt to your exact needs.
Dim wb As Workbook
Set wb = Application.ActiveWorkbook
' Worksheet Receiving the Template Shape ie. the ActiveSheet.
Dim ws As Worksheet
Dim rng As Range
Dim newShape As Shape
Set ws = wb.ActiveSheet
Set rng = ws.Range("B10") ' Destination Cell.
' Worksheet containing the Template Shape.
Dim wsTemplate As Worksheet
Dim shapeToCopy As Shape
Set wsTemplate = wb.Sheets("Template") ' The Worksheet containing template button.
Set shapeToCopy = wsTemplate.shapes("#example") ' The name of template button.
' Different 'Shapes' are created via different Methods, so check the types that you want
' to support and implement the Method as appropriate.
Select Case shapeToCopy.Type
Case MsoShapeType.msoFormControl
' Create the 'new' Shape based on the type and size of the template, and the location of the receiving Cell.
Set newShape = ws.shapes.AddFormControl(shapeToCopy.FormControlType, rng.Left, rng.Top, shapeToCopy.Width, shapeToCopy.Height)
newShape.OLEFormat.Object.Text = shapeToCopy.OLEFormat.Object.Text ' Copy the template buttons caption.
Case Else
' Unsupported Shape Type
Exit Sub
End Select
' Now "Copy" the remaining shared Shape properties that we want to retain from the template.
newShape.Name = shapeToCopy.Name
newShape.AlternativeText = shapeToCopy.AlternativeText
newShape.OnAction = shapeToCopy.OnAction ' The name of the routine to run on button click
' etc...
' etc...
In sheet1 I have an invisible activeX control (Oleobject): commandbutton1
It can be placed alongside the 'hash tag' cell, using:
Sub M_snb()
With Cells.Find("hash tag").Offset(, 1)
Sheet1.CommandButton1.Top = .Top
Sheet1.CommandButton1.Left = .Left
Sheet1.CommandButton1.Visible = True
End With
End Sub

Displaying only a determined range of data

I want to display to the user certain information that exists on a separated worksheet, whenever he clicks a button.
I can set Excel to "go" to this worksheet at the starting line of the range , but I could not find a way to hide everything else.
Is there some method for this, or do I have to hide all rows and columns?
Insert a UserForm in the Workbook's VB Project.
Add a ListBox control to the userform.
Then do something like this code in the UserForm_Activate event code:
Private Sub UserForm_Activate()
Dim tbl As Range
Set tbl = Range("B2:E7") '## Change this to capture the rang you need '
Me.Caption = "Displaying data from " & _
ActiveSheet.Name & "!" & tbl.Address
With ListBox1
.ColumnHeads = False
.ColumnCount = tbl.Columns.Count
.RowSource = tbl.Address
End With
End Sub
Which gives unformatted data from the range:
To export the range as an image, you could create an Image in the UserForm instead of a Listbox. Then this should be enough to get you started.
As you can see from this screenshot, the image might not always come out very clearly. Also, if you are working with a large range of cells, the image might not fit on your userform, etc. I will leave figuring that part out up to you :)
Private Sub UserForm_Activate()
Dim tbl As Range
Dim imgPath As String
Set tbl = Range("B2:E7") '## Change this to capture the rang you need '
imgPath = Export_Range_Images(tbl)
Caption = "Displaying data from " & _
ActiveSheet.Name & "!" & tbl.Address
With Image1
If Not imgPath = vbNullString Then
.Picture = LoadPicture(imgPath)
.PictureSizeMode = fmPictureSizeModeClip
.PictureAlignment = 2 'Center
.PictureTiling = False
.SpecialEffect = 2 'Sunken
End If
End With
End Sub
Function Export_Range_Images(rng As Range) As String
'## Modified by David Zemens with
' credit to: _
' http://vbadud.blogspot.com/2010/06/how-to-save-excel-range-as-image-using.html ##'
Dim ocht As Object
Dim srs As Series
rng.CopyPicture xlScreen, xlPicture
ActiveSheet.Paste
Set ocht = ActiveSheet.Shapes.AddChart
For Each srs In ocht.Chart.SeriesCollection
srs.Delete
Next
'## Modify this line as needed ##'
fname = "C:\users\david_zemens\desktop\picture.jpg"
On Error Resume Next
Kill fname
On Error GoTo 0
ocht.Width = rng.Width
ocht.Height = rng.Height
ocht.Chart.Paste
ocht.Chart.Export Filename:=fname, FilterName:="JPG"
Application.DisplayAlerts = False
ocht.Delete
Application.DisplayAlerts = True
Set ocht = Nothing
Export_Range_Images = fname
End Function
If you record a macro and hide some columns and rows manually, the code will be produced for you, and you will see how it's done.