Loop through points on Chart without using .Activate - vba

I'm trying to loop through a bar graph and make any values above 2 red. The below code is currently working but I want to get around using .Activate
Sub Works()
Dim wbk As Workbook
Dim ws As Worksheet
Dim x As Integer
Set wbk = ThisWorkbook
Set ws = wbk.Worksheets(1)
With ws
.ChartObjects("Chart 1").Activate
For x = 1 To ActiveChart.SeriesCollection(1).Points.Count
If ActiveChart.SeriesCollection(1).Points(x).DataLabel.Caption > 2 Then
'If above 2 make Red
ActiveChart.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
Else
'If below or equal to 2 make Blue
ActiveChart.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(0, 0, 255)
End If
Next x
End With
End Sub
This was my proposed solution but I get a Run-time 438 error when I try to initiate the For loop. I'm assuming it's just a syntax error but I can't figure out how to do it without .Activate
Sub Fails()
Dim wbk As Workbook
Dim ws As Worksheet
Dim x As Integer
Set wbk = ThisWorkbook
Set ws = wbk.Worksheets(1)
With ws.ChartObjects("Chart 1")
For x = 1 To .SeriesCollection(1).Points.Count
If .SeriesCollection(1).Points(x).DataLabel.Caption > 2 Then
'If above 2 make Red
.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
Else
'If below or equal to 2 make Blue
.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(0, 0, 255)
End If
Next x
End With
End Sub

The reason for your error already described to you by #A.S.H in the comments to your post.Series is a property of ChartObject.Chart and not ChartObject.
Try the code below, you could take advantage of VBA's chart capabilities with defining the following types of variables:
Dim ChtObj As ChartObject
Dim Ser As Series
Dim SerPoint As Point
Code
Option Explicit
Sub Fails()
Dim wbk As Workbook
Dim ws As Worksheet
Dim ChtObj As ChartObject
Dim Ser As Series
Dim SerPoint As Point
Set wbk = ThisWorkbook
Set ws = wbk.Worksheets(1)
Set ChtObj = ws.ChartObjects("Chart 1") '<-- set chart object
With ChtObj
Set Ser = .Chart.SeriesCollection(1)
For Each SerPoint In Ser.Points
If SerPoint.DataLabel.Caption > 2 Then 'If above 2 make Red
SerPoint.Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
Else 'If below or equal to 2 make Blue
SerPoint.Format.Fill.ForeColor.RGB = RGB(0, 0, 255)
End If
Next SerPoint
End With
End Sub

As noted in the comment by A.S.H., .Chart is the way to do it. However, you may also declare the chart as a chartObject and use the With myChart.chart in order to get the bonuses from the early binding.
Option Explicit
Sub Fails()
Dim ws As Worksheet
Dim myChart As ChartObject
Dim x As Long
Set ws = ThisWorkbook.Worksheets(1)
Set myChart = ws.ChartObjects("Chart 2")
With myChart.chart
For x = 1 To .SeriesCollection(1).Points.Count
'I have changed a bit the line below, as far as I could not achieve what were you doing...---v
If CLng(.SeriesCollection(1).Name) > 2 Then
.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
Else
.SeriesCollection(1).Points(x).Format.Fill.ForeColor.RGB = RGB(0, 0, 255)
End If
Next x
End With
End Sub

Related

VBA CountA Error 1004

I keep getting Error 1004. I'm not sure how else to declare my object in order to avoid this error:
Sub DeleteBlank()
Dim wb As Workbook
Set wb = ActiveWorkbook
Dim ws As Worksheet
Set ws = wb.Worksheets("Sheet1")
ws.Activate
'Delete Blank Columns
For col = 1 To 4
If WorksheetFunction.CountA(ws.Columns(i)) = 0 Then
ws.Columns.Delete
End If
Next col
End Sub
Step backwards and use col not i. Also, can wrap in a With.
Option Explicit
Sub DeleteBlank()
Dim wb As Workbook
Set wb = ActiveWorkbook
Dim col As Long
Dim ws As Worksheet
Set ws = wb.Worksheets("Sheet1")
With ws
For col = 4 To 1 Step -1
If WorksheetFunction.CountA(.Columns(col)) = 0 Then
.Columns(col).Delete
End If
Next col
End With
End Sub

How to Conditionally Format an Excel SmartArt graphic

I'm working on a project in Excel 2016, and what needs to be done is a chart (preferably a circle) that changes each "slice's" color based on the score. The score is an integer that is pulled from the excel sheet, so it'll change based on the survey results.
Scores go 1 through 5, where 1 is red and 5 is green. I know how to conditionally format the cell itself to be the color I need, but I don't know how to do so on a chart.
I looked into VBA (via a YouTube video), but I can't get that to work either.
Here is the code I have for VBA, if anyone could help me out, or let me know how to do it at all, that would be great!
Private Sub SheetActivate(ByVal Sh As Object)
Dim cht As ChartObject
Dim i As Integer
Dim vntValues As Variant
Dim s As String
Dim mySeries As Series
For Each cht In ActiveSheet.ChartObjects
For Each mySeries In cht.Chart.SeriesCollection
If mySeries.ChartType <> xlPie Then GoTo SkipNotPie
s = Split(mySeries.Formula, ",")(2)
vntValues = mySeries.Values
For i = 1 To UBound(vntValues)
mySeries.Points(i).Interior.Color = Range(s).Cells(i).Interiror.Color
Next i
SkipNotPie:
Next mySeries
Next cht
End Sub
You have mistyping
Range(s).Cells(i).Interiror.Color
and sub procedure variable is not used in your code.
Sub test()
SheetActivate Activesheet '<~~ This will execute the following procedure.
End Sub
Private Sub SheetActivate(ByVal Sh As Object)
Dim cht As ChartObject
Dim i As Integer
Dim vntValues As Variant
Dim s As String
Dim mySeries As Series
For Each cht In Sh.ChartObjects
For Each mySeries In cht.Chart.SeriesCollection
If mySeries.ChartType <> xlPie Then GoTo SkipNotPie
s = Split(mySeries.Formula, ",")(2)
vntValues = mySeries.Values
For i = 1 To UBound(vntValues)
mySeries.Points(i).Interior.Color = Range(s).Cells(i).Interior.Color
Next i
SkipNotPie:
Next mySeries
Next cht
End Sub
If your cell color is conditionally formated then change like this
For i = 1 To UBound(vntValues)
'mySeries.Points(i).Interior.Color = Range(s).Cells(i).Interior.Color
mySeries.Points(i).Interior.Color = Range(s).Cells(i).FormatConditions(1).Interior.Color
Next i

How to use two workbooks and their worksheets altogether

I am using two workbooks at a time. The first workbook is the current workbook and the second will be opened while programming execution. I have made the global objects of the workbooks and worksheets. I'm having issues with using the worksheets simultaneously. The error is ERROR: object variable or with block variable, not set. I have mentioned the error in the comment in the second subroutine.
Dim WB As Workbook
Dim WB2 As Workbook
Dim WKS As Worksheet
Dim WKS2 As Worksheet
Sub Button1_Click()
Dim fd As Office.FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)
Set WB = ThisWorkbook
WB.Activate
fd.AllowMultiSelect = False
fd.Title = "Provide a title here."
fd.InitialFileName = ThisWorkbook.Path
If fd.Show() = True Then
strFile = fd.SelectedItems(1)
Set WB2 = Workbooks.Open(strFile)
Set WSK2 = WB2.Sheets("Schweitzer Analysis")
CalculateGrades
Else
MsgBox ("No file selected")
End If
End Sub
Sub CalculateGrades()
' first clear the contents where grades results can appear
clearGradesContents
Dim index As Integer ' for current sheet
Dim index2 As Integer ' for student sheet
Dim countCorrect As Integer ' to count no of correct answers
index = 2
index = 8
countCorrect = 0
' this first error here
' ERROR: object variable or with block variable not set
Set WKS = WB.ActiveSheet
Do While index <= 21
' the SECOND error shows here
If WKS.Cells(index, 2) = WKS2.Cells(index2, 3) Then
Count = Count + 1
Else
WKS.Cells(index, 5) = WKS2.Cells(index2, 3)
End If
If WKS.Cells(index, 3) = WKS2.Cells(index2, 4) Then
Count = Count + 1
Else
WKS.Cells(index, 6) = WKS2.Cells(index2, 4)
End If
index2 = index2 + 1
index = index + 1
Loop
End Sub
Sub clearGradesContents()
Range("E2:F21").Select
Selection.ClearContents
Range("I2:I3").Select
Selection.ClearContents
End Sub
EDIT: sorry for the previous answer, I hadn't seen your global declarations on top and I was sure it was because you hadn't declare it.
I think the issue is the following:
[...]
Set WB = ThisWorkbook
WB.Activate '<-- WB is the active workbook now
[...]
Set WB2 = Workbooks.Open(strFile) '<-- WB2 is the active workbook now!
Set WSK2 = WB2.Sheets("Schweitzer Analysis")
CalculateGrades '<-- going to next macro
[...]
Set WKS = WB.ActiveSheet '<-- ERROR: WB2 is the active workbook. WB can't have an active sheet if it's not the active workbook
Then of course, being WKS not correctly set, you can't execute this:
If WKS.Cells(index, 2) = WKS2.Cells(index2, 3) Then
I have no way to test it now, but I think that should be the issue.
To solve it,
If you really need WKS to be the ActiveSheet
Dim activeSheetName As String
...
Sub Button1Click()
...
Set WB = ThisWorkbook
WB.Activate
activeSheetName = WB.ActiveSheet.Name
...
End Sub
Sub CalculateGrades()
...
Set WKS = WB.Sheets(activeSheetName)
...
If you already know the name of the sheet you need
Just write:
Set WKS = WB.Sheets("your sheet")
instead of
Set WKS = WB.ActiveSheet

Copy Worksheets break links

I have the below 2 subs in VBA which perform 2 different but similar tasks. One allows you to selects sheets from a Workbook using a checkbox popup and then copies these sheets into a new blank Workbook. The other allows you to manually populate a list of names of the sheets you want to copy (i.e. setup a "batch" of sorts) on a sheet and then copy all the sheets across into a new blank Workbook in a similar fashion to the first.
The problem I am having is - with the first sub I am able to break links after copying into the new Workbook, but with the second sub I am not able to break links. I think it has to do with a number of defined names within the original Workbook, as if you "Move or Copy/Create a Copy" manually, you are able to break the links.
Is there any code I can add to the below (onto both subs if possible) which will automatically break all links in the new Workbook to the old one? Or at least, is it possible to amend the second sub so that it copies across in a similar fashion to the first one which will then allow me to break links manually?
Sub CopySelectedSheets()
'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
Dim intWidth As Integer
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 firstSelected As Boolean
' 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)
'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
'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
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
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 CopySpecificSheets()
'1. Declare Variables
Dim myArray() As String
Dim myRange As Range
Dim Cell As Range
Dim OldBook As String
Dim newBook As String
Dim a As Long
'2. Set Range of Lookup
Set myRange = Sheets("Report Batch").Range("A2:A40")
OldBook = ActiveWorkbook.Name
'3. Generate Array of Sheet Names removing Blanks
For Each Cell In myRange
If Not Cell = "" Then
a = a + 1
ReDim Preserve myArray(1 To a)
myArray(a) = Cell
End If
Next
'4. Copy Array of Sheets to new Workbook
For a = 1 To UBound(myArray)
If a = 1 Then
Sheets(myArray(a)).Copy
newBook = ActiveWorkbook.Name
Workbooks(OldBook).Activate
Else
Sheets(myArray(a)).Copy After:=Workbooks(newBook).Sheets(a - 1)
Workbooks(OldBook).Activate
End If
Next
End Sub
Try something like this:
Sub CopySpecificSheets()
'1. Declare Variables
Dim rngData As Range
Dim arrData As Variant
Dim arrSheets() As String
Dim lSheetCount As Long
Dim i As Long
Dim j As Long
'2. Initialize variables
Set rngData = Sheets("Report Batch").Range("A2:A40")
arrData = rngData.Value
lSheetCount = WorksheetFunction.CountA(rngData)
ReDim arrSheets(lSheetCount - 1)
'3. Fill the array with non blank sheet names
For i = LBound(arrData) To UBound(arrData)
If arrData(i, 1) <> vbNullString Then
arrSheets(j) = arrData(i, 1)
j = j + 1
End If
' early break if we have all the sheets
If j = lSheetCount Then
Exit For
End If
Next i
'4. Copy the sheets in one step
Sheets(arrSheets).Copy
End Sub
Thanks
This isn't tested, but I think if you add in a subroutine to your source workbook VBA code like this:
Sub BreakLinks(ByRef wb As Workbook)
Dim Links As Variant
Dim i As Long
On Error Resume Next
Links = wb.LinkSources(Type:=xlLinkTypeExcelLinks)
On Error GoTo 0
If Not IsEmpty(Links) Then
For i = 1 To UBound(Links)
wb.BreakLink Name:=Links(i), _
Type:=xlLinkTypeExcelLinks
Next i
End If
End Sub
And then call it after you copy the sheets to the new workbook
Call BreakLinks(newBook)
That should achieve the desired effect of severing those links. Just be sure the links are broken to any sort of Save or SaveAs operation so that the broken links are maintained.

VBA export multiple charts (4 each time) from the same sheet into one powerpoint slide

I've been trying to export multiple excel charts into powerpoint but there is a catch...I'd like to export 4 charts into a single slide at a time.
I've found the following code but it needs to be modify so that 4 charts are exported into one slide, instead of a single chart per slide.
The code is below:
Thanks!
Sub PushChartsToPPT()
Dim ppt As PowerPoint.Application
Dim pptPres As PowerPoint.Presentation
Dim pptSld As PowerPoint.Slide
Dim pptCL As PowerPoint.CustomLayout
Dim pptShp As PowerPoint.Shape
Dim cht As Chart
Dim ws As Worksheet
Dim i As Long
'Get the PowerPoint Application object:
Set ppt = CreateObject("PowerPoint.Application")
ppt.Visible = msoTrue
Set pptPres = ppt.Presentations.Add
'Get a Custom Layout:
For Each pptCL In pptPres.SlideMaster.CustomLayouts
If pptCL.Name = "Title and Content" Then Exit For
Next pptCL
'Copy ALL charts embedded in EACH WorkSheet:
For Each ws In ActiveWorkbook.Worksheets
For i = 1 To ws.ChartObjects.Count
Set pptSld = pptPres.Slides.AddSlide(pptPres.Slides.Count + 1, pptCL)
pptSld.Select
For Each pptShp In pptSld.Shapes.Placeholders
If pptShp.PlaceholderFormat.Type = ppPlaceholderObject Then Exit For
Next pptShp
Set cht = ws.ChartObjects(i).Chart
cht.ChartArea.Copy
ppt.Activate
pptShp.Select
ppt.Windows(1).View.Paste
Next i
Next ws
End Sub
Try this:
For Each ws In ActiveWorkbook.Worksheets
For i = 1 To ws.ChartObjects.Count Step 4 'your count must be a multiple of four other it wouldn't work
Set pptSld = pptPres.Slides.AddSlide(pptPres.Slides.Count + 1, pptCL)
pptSld.Select
For Each pptShp In pptSld.Shapes.Placeholders
If pptShp.PlaceholderFormat.Type = ppPlaceholderObject Then Exit For
Next pptShp
For j = 0 to 3
Set cht = ws.ChartObjects(i+j).Chart
cht.ChartArea.Copy
ppt.Activate
pptShp.Select
ppt.Windows(1).View.Paste
Next J
Next i