I created a macro that add an arrow to chartPoint if value of that point is under 1 and different from 0
The code works perfectly but When Itry to create The Arrow I get an error "Object Required" and I didn't manage to select the head of that shape and create arrow there .
What I'm trying to do is described in the Image below
The code of Verifying Result and Add arrow is below
Sub fzerfgsdf()
'
' fzerfgsdf Macro
'
Dim clLeft As Double
Dim clTop As Double
Dim clWidth As Double
Dim clHeight As Double
Dim cl As Range
Dim shpOval As Shape
ActiveSheet.ChartObjects("Graphique 69").Activate
x = ActiveChart.SeriesCollection(1).Values
For i = LBound(x) To UBound(x)
Debug.Print "Point "; i; "="; x(i)
If x(i) < 1 And x(i) <> 0 Then
ActiveChart.SeriesCollection(1).Points(i).Select
Set cl = ActiveChart.SeriesCollection(1).Points(i).Select '<-- Range("C2")
clLeft = cl.Left
clTop = cl.Top
clHeight = 131.25
clWidth = 579
Set shpOval = ActiveSheet.Shapes.AddConnector(msoConnectorStraight, clLeft, clTop, 579, 131.25)
shpOval.Select
selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadOpen
selection.ShapeRange.ShapeStyle = msoLineStylePreset20
End If
Next i
End Sub
I found solution for first problem which is creating shape but I can't figure out how to locate that shape in the right place where the blue chart using a specific values see image
Sub fzerfgsdf()
'
' fzerfgsdf Macro
'
ActiveSheet.ChartObjects("Graphique 69").Activate
x = ActiveChart.SeriesCollection(1).Values
For i = LBound(x) To UBound(x)
Debug.Print "Point "; i; "="; x(i)
If x(i) < 1 And x(i) <> 0 Then
ActiveSheet.ChartObjects("Graphique 69").Activate
ActiveChart.SeriesCollection(1).Points(i).Select
Dim clLeft As Double
Dim clTop As Double
Dim clWidth As Double
Dim clHeight As Double
Dim cl As Point
Dim shpOval As Shape
Set cl = ActiveChart.SeriesCollection(1).Points(i) '<-- Range("C2")
clLeft = cl.Left
clTop = cl.Top
clHeight = 131.25
clWidth = 579
Set shpOval = ActiveSheet.Shapes.AddConnector(msoConnectorStraight,
clLeft,
clTop, 579, 131.25)
shpOval.Select
selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadOpen
selection.ShapeRange.ShapeStyle = msoLineStylePreset20
End If
Next i
End Sub
Could Anyone light in solving This ?
Best Regards
Polos
Public Sub fzerfgsdf()
'
' fzerfgsdf Macro
'
Dim ws As Excel.Worksheet
Dim chrt As Excel.Chart
Dim sries As Excel.Series
Dim x As Variant
Dim clLeft As Double, clTop As Double
Dim clWidth As Double, clHeight As Double
Dim clBeginX As Double, clBeginY As Double, clEndX As Double, clEndY As Double
Dim cl As Excel.Point
Dim shpOval As Excel.Shape
Dim dl As Excel.DataLabel
Dim i As Long
clHeight = 30
clWidth = 15
Set ws = Application.ActiveSheet
Set chrt = ws.ChartObjects("Graphique 69").Chart
Set sries = chrt.SeriesCollection(1)
x = sries.Values
For i = LBound(x) To UBound(x)
Debug.Print "Point "; i; "="; x(i)
If (x(i) < 1) And (x(i) <> 0) Then
Set cl = sries.Points(i)
With chrt.ChartArea
clBeginX = IIf(.Left + cl.Left - clWidth < 0, 0, .Left + cl.Left - clWidth)
clBeginY = IIf(.Top + cl.Top - clHeight < 0, 0, .Top + cl.Top - clHeight)
clEndX = .Left + cl.Left
clEndY = .Top + cl.Top
End With
Set shpOval = ws.Shapes.AddConnector(msoConnectorStraight, clBeginX, clBeginY, clEndX, clEndY)
shpOval.Line.EndArrowheadStyle = msoArrowheadOpen
shpOval.ShapeStyle = msoLineStylePreset20
cl.HasDataLabel = True
sries.HasLeaderLines = False
Set dl = cl.DataLabel
With dl
.Text = "RFT 93%=> 5P"
.Position = xlLabelPositionAbove
.Format.AutoShapeType = msoShapeRectangularCallout
.Format.Line.Visible = msoFalse
.Top = cl.Top - clHeight - .Height - 5
.Left = cl.Left - clWidth - (.Width / 2)
With .Format.TextFrame2.TextRange.Font
.Size = 12
.Fill.ForeColor.RGB = RGB(255, 0, 0)
.Bold = msoTrue
End With
End With
End If
Next
Set shpOval = Nothing
Set cl = Nothing
Set sries = Nothing
Set chrt = Nothing
Set ws = Nothing
End Sub
Related
I'm relatively new to VBA.
I'm helping an internal team to improve workflow by reducing errors when copying and pasting data from Excel to PowerPoint.
How do I configure conditional formatting in the PowerPoint table based on two rules?
2-Color Scale:
Icon Set:
My current code is as follows. This has allowed for data to be copied from a single Excel cell to a single PowerPoint table cell.
Sub TableData()
Dim oPPApp As Object, oPPrsn As Object, oPPSlide As Object
Dim oPPShape As Object
Dim FlName As String
FlName = "FILE PATH"
On Error Resume Next
Set oPPApp = GetObject(, "PowerPoint.Application")
If Err.Number <> 0 Then
Set oPPApp = CreateObject("PowerPoint.Application")
End If
Err.Clear
On Error GoTo 0
oPPApp.Visible = True
Set oPPrsn = oPPApp.Presentations.Open(FlName)
Set oPPSlide = oPPrsn.Slides(1)
Set oPPShape = oPPSlide.Shapes(2)
ThisWorkbook.Sheets("Sheet1").Activate
oPPShape.Table.Cell(2, 3).Shape.TextFrame.TextRange.Text = Range("C1").Value
End Sub
For the table creation, the code is based on this answer I received, so you may have to adjust the code to your liking (it creates a slide for each row), the dimensions of the source and target table are based on my needs, since your code works only for one cell I wanted to test with more data.
The relevant part for your request is anyway this one:
Dim RGB_Val As String
RGB_Val = rng(i).DisplayFormat.Interior.Color
newTable.cell(i, 2).Shape.Fill.ForeColor.RGB = RGB_Val
that takes the background color of Conditionally Formatted cells (so you would have to apply conditional formatting to the Excel table, first) (information gathered from this answer and this one taking care of the comment Jun 9, 2018 at 13:08).
In each slide created following the row, each Excel column cell will then become a row in PPT, on the second column, while on the first there will be the circles, whose size can be defined by FlowRatio's value.
Their colors will have to be defined as per below (values and colors taken from your screenshot):
If rng(i).Value >= 12 Then
ElseIf rng(i).Value < 12 And rng(i).Value >= -12 Then
ElseIf rng(i).Value < -12 Then
So, from this:
you will get this:
but as per above, you may have to adjust sizes of the ranges to avoid errors.
Ah, it runs from Excel to PPT.
Sub CopyConditionalFormattingAddChecks()
Dim DataRange As Range, DataRow As Range, rng As Range, i As Long, col As Long
Dim ppApp As PowerPoint.Application, pres As PowerPoint.Presentation
Dim sld As PowerPoint.slide, newTable As PowerPoint.Table, Sldss As Slides
Dim CellLeft As Single
Dim CellTop As Single
Dim CellWidth As Single
Dim CellHeight As Single
Dim CellWidth_2 As Single
Dim Shp_Cntr As Single
Dim Shp_Mid As Single
Dim CircleCheck As PowerPoint.Shape
Dim IconNavigator As Object
Dim RGB_Val As String
Dim SlideCounter As Integer
Dim FlowCounter As Integer
Dim IconCounter As Integer
Dim TestCounter As Integer
FlowRatio = 0.6
On Error Resume Next
Set ppApp = GetObject(, "PowerPoint.Application")
Set pres = ppApp.ActivePresentation
On Error GoTo 0
If pres Is Nothing Then
MsgBox "Destination presentation must be open in PowerPoint", vbCritical
Exit Sub
End If
SlideCounter = 0
CircleCheckCounter = 0
Set DataRange = Selection
For Each DataRow In DataRange.Rows
Set sld = pres.Slides.AddSlide(pres.Slides.Count + 1, pres.SlideMaster.CustomLayouts(2))
SlideCounter = SlideCounter + 1
Set newTable = sld.Shapes.AddTable(14, 4).Table ' different here
With newTable.Columns(1): .Width = 5: End With
With newTable.Columns(2): .Width = 60: End With
With newTable.Columns(3): .Width = 5: End With
With newTable.Columns(4): .Width = 50: End With
With sld.Shapes.Placeholders(2): .Width = 550: End With
Set rng = DataRow.Cells(1).Resize(1, 10)
For i = 1 To newTable.Rows.Count
newTable.cell(i, 2).Shape.TextFrame2.TextRange.Text = rng.Cells(i).Value
RGB_Val = rng(i).DisplayFormat.Interior.Color
newTable.cell(i, 2).Shape.Fill.ForeColor.RGB = RGB_Val
If rng(i).Value >= 12 Then
CellTop = newTable.cell(i, 1).Shape.Top
CellLeft = newTable.cell(i, 1).Shape.Left
CellWidth = newTable.cell(i, 1).Shape.Width
CellHeight = newTable.cell(i, 1).Shape.Height
Shp_Cntr = CellLeft + CellWidth / 2
Shp_Mid = CellTop + CellHeight / 2
Set CircleCheck = sld.Shapes.AddShape(Type:=msoShapeOval, Left:=Shp_Cntr - (CellHeight * FlowRatio / 2), Top:=Shp_Mid - (CellHeight * FlowRatio / 2), Width:=CellHeight * FlowRatio, Height:=CellHeight * FlowRatio)
CircleCheck.Fill.ForeColor.RGB = RGB(214, 85, 50)
CircleCheck.Line.Weight = 0.75
CircleCheck.Line.ForeColor.RGB = RGB(255, 255, 255)
CircleCheck.Line.Visible = msoTrue
CircleCheck.LockAspectRatio = msoTrue
CircleCheck.Name = "CircleCheck " & CircleCheckCounter
CircleCheck.ZOrder msoBringToFront
ElseIf rng(i).Value < 12 And rng(i).Value >= -12 Then
CellTop = newTable.cell(i, 1).Shape.Top:
CellLeft = newTable.cell(i, 1).Shape.Left
CellWidth = newTable.cell(i, 1).Shape.Width
CellHeight = newTable.cell(i, 1).Shape.Height
Shp_Cntr = CellLeft + CellWidth / 2
Shp_Mid = CellTop + CellHeight / 2
'
Set CircleCheck = sld.Shapes.AddShape(Type:=msoShapeOval, Left:=Shp_Cntr - (CellHeight * FlowRatio / 2), Top:=Shp_Mid - (CellHeight * FlowRatio / 2), Width:=CellHeight * FlowRatio, Height:=CellHeight * FlowRatio)
CircleCheck.Fill.ForeColor.RGB = RGB(234, 194, 130)
CircleCheck.Line.Weight = 0.75
CircleCheck.Line.ForeColor.RGB = RGB(255, 255, 255)
CircleCheck.Line.Visible = msoTrue
CircleCheck.LockAspectRatio = msoTrue
CircleCheck.Name = "CircleCheck " & CircleCheckCounter
CircleCheck.ZOrder msoBringToFront
ElseIf rng(i).Value < -12 Then
CellTop = newTable.cell(i, 1).Shape.Top:
CellLeft = newTable.cell(i, 1).Shape.Left
CellWidth = newTable.cell(i, 1).Shape.Width
CellHeight = newTable.cell(i, 1).Shape.Height
Shp_Cntr = CellLeft + CellWidth / 2
Shp_Mid = CellTop + CellHeight / 2
Set CircleCheck = sld.Shapes.AddShape(Type:=msoShapeOval, Left:=Shp_Cntr - (CellHeight * FlowRatio / 2), Top:=Shp_Mid - (CellHeight * FlowRatio / 2), Width:=CellHeight * FlowRatio, Height:=CellHeight * FlowRatio)
CircleCheck.Fill.ForeColor.RGB = RGB(104, 164, 144)
CircleCheck.Line.Weight = 0.75
CircleCheck.Line.ForeColor.RGB = RGB(255, 255, 255)
CircleCheck.Line.Visible = msoTrue
CircleCheck.LockAspectRatio = msoTrue
CircleCheck.Name = "CircleCheck " & CircleCheckCounter
CircleCheck.ZOrder msoBringToFront
End If
Next i
Next DataRow
End Sub
Thanks #Rory! I was able to fix it and now I got a new bug
Run time error 1004/parameter not valid
at at .SeriesCollection(j).XValues = ws.Range(rs)
Could someone please help me?
'''
I am trying to make multiple charts. And each chart would have 20 different groups with legend.
The way I have tried is first make multiple charts by columns and then add for/n loop in my code (here tried to have every 20 rows for one each group
Sub horizontal()
Dim sh As Shape
Dim ws As Worksheet
Dim ch As Chart
Dim rd As Range
Dim i As Integer, j As Integer, k As Integer
Set ws = Sheets("S1")
On Error Resume Next
ws.ChartObjects.Delete
On Error GoTo 0
For i = 20 To 45
Set rs = ws.Range("s2:s21")
Set rd = ws.Range("f1:j10")
Set sh = ws.Shapes.AddChart2(240, xlXYScatterLines)
Set ch = sh.Chart
For j = 1 To 20
k = j * 20
With ch 'shape.chart'
.SetSourceData Union(rs, ws.Range(ws.Cells(2, i), ws.Cells(21, i)))
.SeriesCollection.NewSeries
.SeriesCollection(j).XValues = ws.Range("s2:s21")
.SeriesCollection(j).Values = ws.Range(ws.Cells(k - 18, i), ws.Cells(k + 1, i))
.HasTitle = True
.ChartTitle.Text = ws.Range("T1")
.HasLegend = True
End With
Next j
With sh
.Name = "cht" & (i - 19)
.Top = (i - 20) * rd.Height
.Left = rd.Left
.Width = rd.Width
.Height = rd.Height
End With
Next i
End Sub
I tried the below two codes but they didn'twork.
Sub horizontal()
Dim sh As Shape
Dim ws As Worksheet
Dim ch As Chart
Dim rd As Range
Dim i As Integer, j As Integer, k As Integer
Set ws = Sheets("S1")
On Error Resume Next
ws.ChartObjects.Delete
On Error GoTo 0
For i = 20 To 45
Set rs = ws.Range("s2:s21")
Set rd = ws.Range("f1:j10")
Set sh = ws.Shapes.AddChart2(240, xlXYScatterLines)
Set ch = sh.Chart
With ch 'shape.chart'
.SetSourceData Union(rs, Range(Cells(2, i), Cells(21, i)))
.HasTitle = True
.ChartTitle.Text = ws.Range("T1")
.HasLegend = True
End With
With sh
.Name = "cht" & (i - 19)
.Top = (i - 20) * rd.Height
.Left = rd.Left
.Width = rd.Width
.Height = rd.Height
End With
Next i
End Sub
Sub diameter()
Dim ws As Worksheet
Dim sh As Shape
Dim ch As Chart
Dim rng As Range, rngTime As Range
Dim n As Integer, m As Integer, k As Integer, i As Integer
Set ws = Sheets("S1")
'delete previous plots
If ws.ChartObjects.Count > 0 Then
ws.ChartObjects.Delete
End If
Set rngTime = ws.Range(Cells(2, 19), Cells(21, 19))
ws.Shapes.AddChart2(240, xlXYScatterLines).Select
ws.Shapes(1).Chart.SetSourceData Union(rngTime, Range(Cells(2, 20), Cells(21, 20)))
'Source:=Range("'S1'!$S$2:$S$21,'S1'!$T$2:$T$21")
For n = 1 To 20
m = n * 20
With ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(n).XValues = ws.Range(Cells(2, 19), Cells(21, 19))
ActiveChart.FullSeriesCollection(n).Values = ws.Range(Cells(m - 18, 20), Cells(m + 1, 20))
End With
Next n
End Sub
'''
I want to resize the shape of rectangle according to cell data, were height(width) of rectangle is constant and length changes according to cell References.
For EG (please refer image) : DW1 is starting side which should have Reference data from range("B13") and move along or match data to range("D4:AF4") and it should be same for another end side DW2.
DW2 should have reference from range("C13") and match data to range("D4:AF4").
I have worked on some code but it is not having proper output.
Please have a look for my code below.
new code will also be helpfull
Sub Rectanglematch()
Dim dl1 As Double
Dim dl2 As Double
Dim dw1 As Double
Dim dw2 As Double
Dim dw As Double
Dim dl As Double
Dim d As Date
Dim R As Excel.Range
dw = dw1
dw = dw2
dl = dl1
dl = dl2
d = CDate(Sheets("Tabelle1").Range("b13"))
Set R = Sheets("Tabelle1").Range("d4:AF4")
dl1 = 10 * Range("A1").Value
dl2 = 10 * Range("A1").Value
dw1 = Application.WorksheetFunction.Match(CDbl(CDate(Sheets("Tabelle1").Range("b13"))), R, 0)
dw2 = Application.WorksheetFunction.Match(CDbl(CDate(Sheets("Tabelle1").Range("c13"))), R, 0)
With ActiveSheet.Shapes("Rechteck 2")
.Top = .Top - dw + .Height
.Height = dw
.Width = dl
End With
End Sub
I'm not really sure if I got your point in 100%, but take a look at my approach to this:
Option Explicit
Sub Rectanglematch()
Dim lastRow As Long
Dim lastCol As Long
Dim heightCell As Long
Dim widthCell As Long
Dim rngDates As Range
Dim i As Long
Dim sDat As Long
Dim eDat As Long
Dim myRectangle As Shape
With ThisWorkbook.Sheets("Tabelle1")
lastRow = .Cells(Rows.Count, 2).End(xlUp).Row
lastCol = .Cells(4, Columns.Count).End(xlToLeft).Column
For i = 6 To lastRow
If .Cells(i, 2) = "" Or .Cells(i, 3) = "" Then
Else
heightCell = .Cells(i, 2).RowHeight
widthCell = .Cells(i, 2).Width
Set rngDates = .Range(.Cells(4, 4), .Cells(4, lastCol))
sDat = Application.WorksheetFunction.Match(.Cells(i, 2), rngDates, 0) + 3
eDat = Application.WorksheetFunction.Match(.Cells(i, 3), rngDates, 0) + 3
Set myRectangle = .Shapes.AddShape(msoShapeRectangle, .Cells(i, sDat).Left, .Cells(i, sDat).Top, .Cells(i, eDat).Left - .Cells(i, sDat).Left, heightCell)
End If
Next i
End With
End Sub
And the result looks like this:
Hope it will help You :)
As the question say I made a simple macro that takes a couple sheets of data and turns it into charts. The marco works perfectly on my laptop however, when I email my boss the workbook containing the macro it does not work. If i remember correctly it threw error 42 or something.
The line in my code it highlighted was the line that set the title of the graph. I feed a function that creates my graphs titles from an array that gets looped through via For loop. Once i changed it to a different text value and took out the array it worked.
Button that controls macro
Private Sub CommandButton1_Click()
Dim selectedWorksheet As Worksheet
Dim numItems As Integer
Dim numOfIdentifiers As Integer
Dim identArray() As String
Dim itemsFoundL() As String
Dim itemsSoldL() As Integer
Dim identifiers As Range
Dim ident As Range
Dim upperBound As String
Dim lowerBound As String
Dim foodLBound As String
Dim foodUBound As String
Dim nonFoodLBound As String
Dim nonFoodUBound As String
Dim foodNameColumn As String
Dim foodSoldColumn As String
Dim nonFoodNameColumn As String
Dim nonFoodSoldColumn As String
Dim sheetName As Range
Dim i As Integer
upperBound = Range("J1").Value
lowerBound = Range("I1").Value
foodLBound = Range("I3").Value
foodUBound = Range("J3").Value
nonFoodLBound = Range("I4").Value
nonFoodUBound = Range("J4").Value
foodNameColumn = Range("I5").Value
foodSoldColumn = Range("I6").Value
nonFoodNameColumn = Range("I8").Value
nonFoodSoldColumn = Range("I9").Value
Set sheetName = Range("I2")
Set identifiers = Range(lowerBound, upperBound)
Set selectedWorksheet = ActiveWorkbook.Worksheets(CStr(sheetName))
numOfIdentifiers = identifiers.Count
ReDim identArray(0 To numOfIdentifiers - 1)
i = 0
For Each ident In identifiers
identArray(i) = ident.Value
i = i + 1
Next ident
For i = 0 To numOfIdentifiers - 1
numItems = CInt(numberOfItems(selectedWorksheet, identArray(i), foodLBound, foodUBound))
If numItems = 0 Then
numItems = CInt(numberOfItems(selectedWorksheet, identArray(i), nonFoodLBound, nonFoodUBound))
numItems = numItems - 1
ReDim itemsFoundL(0 To numItems)
ReDim itemsSoldL(0 To numItems)
itemsFoundL = itemsFound(selectedWorksheet, identArray(i), nonFoodLBound, nonFoodUBound, numItems, nonFoodNameColumn)
itemsSoldL = itemsSoldFound(selectedWorksheet, identArray(i), nonFoodLBound, nonFoodUBound, numItems, nonFoodSoldColumn)
Else
numItems = numItems - 1
ReDim itemsFoundL(0 To numItems)
ReDim itemsSoldL(0 To numItems)
itemsFoundL = itemsFound(selectedWorksheet, identArray(i), foodLBound, foodUBound, numItems, foodNameColumn)
itemsSoldL = itemsSoldFound(selectedWorksheet, identArray(i), foodLBound, foodUBound, numItems, foodSoldColumn)
End If
Call CreateCharts(itemsFoundL, itemsSoldL, identArray(i), numItems)
Next i
End Sub
Chart creating helper function
Function CreateCharts(items As Variant, itemsSold As Variant, chartTitle As String, itemCount As Integer)
If itemCount > 0 Then
Charts.Add
ActiveChart.ChartType = xlBarStacked
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(1).XValues = items
ActiveChart.SeriesCollection(1).Values = itemsSold
ActiveChart.ChartArea.Format.TextFrame2.TextRange.Font.Name = "Arial"
With ActiveChart
.HasLegend = False
.HasTitle = True
.chartTitle.Text = chartTitle
End With
Else
End If
End Function
Here's the relevant code in the BurnUpApplication Module:
For Each slice In slices
graph.drawSlice slice
Next
Which calls the following code in a CBurnUp class:
Public Sub drawSlice(slice As CSlice)
With self.SeriesCollection.Add(slice.CumulativeSizeRange())
.xValues = mXAxis
.name = slice.name
.Format.Line.ForeColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
End With
sliceCounter = sliceCounter + 1
End Sub
Which calls the following code in CSlice class:
Public Property Get CumulativeSizeRange() As Range
Set CumulativeSizeRange = mSizeCumulative
End Property
This works fine in Excel 2011!!
Here's the full project code:
BurnUpApplication Module
Option Explicit
Dim graph As CBurnUp
Const PROJECT_WS_NAME = "Project"
Const TEMPLATE_WS_NAME = "Template"
Const DATA_TABLE_WS_NAME = "DataTable"
Const BURNUP_WS_NAME = "BurnUp"
Sub onClick_UpdateBurnUp()
WaitDialog.Show vbModeless
End Sub
Sub UpdateBurnup()
Dim projectWs As Worksheet
Set projectWs = Worksheets(PROJECT_WS_NAME)
UpdateDataTableSlices projectWs, Worksheets(DATA_TABLE_WS_NAME)
Set graph = New CBurnUp
graph.init Worksheets(BURNUP_WS_NAME).ChartObjects(1).Chart
Dim slice As CSlice
Dim slices As Collection
Dim dateRange As Range
graph.clean
With projectWs
graph.XAxis = Union(getVisibleDataFrom(.Columns(1)), _
getVisibleDataFrom(.Columns(2)), _
getVisibleDataFrom(.Columns(3)))
Set dateRange = getVisibleDataFrom(.Columns(3))
End With
Set slices = getSliceList
For Each slice In slices
graph.drawSlice slice
Next
graph.drawForecast getVisibleDataFrom(Worksheets(DATA_TABLE_WS_NAME).Columns(getProperty("CURRENT_FORECAST_COLUMN")))
graph.drawBurnedPoints getVisibleDataFrom(Worksheets(DATA_TABLE_WS_NAME).Columns(getProperty("BURNED_POINTS_COLUMN")))
drawPlannedDatesAndContingency graph, slices, getPlannedBurnUp
For Each slice In slices
graph.drawSliceName slice
graph.drawReleaseDate slice, dateRange
graph.drawMilestoneDate slice, dateRange
Next
Unload WaitDialog
Worksheets("BurnUp").Activate
End Sub
Sub onClick_SetPlan()
Dim currentForecast As Range
Dim plannedBurnup As Range
If getProperty("IS_PROJECT_STARTED") = True Then
Dim dialogResult As VbMsgBoxResult
dialogResult = MsgBox("You have choosen to Set a new Baseline. Contingency and expectations will be changed accoring to the current velocity. Do you want to continue?", vbYesNo, "Burnup - Rebase")
If dialogResult = vbNo Then
Exit Sub
End If
End If
'take forecasted burnup
Set currentForecast = getDataFrom(Worksheets(DATA_TABLE_WS_NAME).Columns(getProperty("CURRENT_FORECAST_COLUMN")))
'copy to Planned BurnUp
Set plannedBurnup = getDataFrom(Worksheets(PROJECT_WS_NAME).Columns(getProperty("PLANNED_FORECAST_COLUMN")))
currentForecast.Copy
plannedBurnup.PasteSpecial xlPasteValues
' if there is a sampled average velocity change the avg
updatePlannedVelocityWithSampledOne
updateSliceSize Worksheets(PROJECT_WS_NAME)
'the planned velocity
End Sub
Sub updatePlannedVelocityWithSampledOne()
Dim plannedVelPerEng As Range
Dim sampledVelPerEng As Range
Set plannedVelPerEng = Worksheets(PROJECT_WS_NAME).Cells(getProperty("PLANNED_VEL_ROW"), getProperty("PLANNED_VEL_COL"))
Set sampledVelPerEng = Worksheets(PROJECT_WS_NAME).Cells(getProperty("SAMPLED_VEL_ROW"), getProperty("SAMPLED_VEL_COL")) ''
If Not WorksheetFunction.IsNA(sampledVelPerEng) Then
plannedVelPerEng.Value = sampledVelPerEng.Value
End If
End Sub
Sub updateSliceSize(inWs As Worksheet)
Dim isStart, isEnd As Byte
Dim iCursor As Byte
Dim currentIteration As Byte
Dim firstDataRow As Byte
firstDataRow = getProperty("FIRST_DATA_ROW")
isStart = getProperty("PROJECT_SLICES_START_COLUMN") + 1
isEnd = getProperty("PROJECT_SLICES_END_COLUMN")
currentIteration = getProperty("CURRENT_ITERATION_INDEX") + firstDataRow
iCursor = isStart
Do While iCursor < isEnd
inWs.Cells(2, iCursor).Formula = "=" & inWs.Cells(currentIteration, iCursor).Address(False, False)
inWs.Cells(currentIteration, iCursor).Interior.Color = inWs.Cells(firstDataRow, iCursor).Interior.Color
iCursor = iCursor + 1
Loop
End Sub
Sub drawPlannedDatesAndContingency(burnUp As CBurnUp, slices As Collection, plannedBurnup As Range)
Dim s As CSlice
Dim i As Byte
Dim contingencySize As Double
Dim currentIteration As Byte
currentIteration = 0
For Each s In slices
s.StartingIteration = currentIteration
For i = 1 To plannedBurnup.Count
If (plannedBurnup.Cells(i).Value >= s.CumulativeWithContingency) Then
currentIteration = i
If s.contingency > 0 Then
contingencySize = getContingencySize(s, plannedBurnup, s.StartingIteration, currentIteration)
burnUp.drawContingency i, s.Cumulative(i), contingencySize
End If
Exit For
End If
Next
Next
End Sub
Function getContingencySize(slice As CSlice, plannedBurnup As Range, startIt As Byte, endIt As Byte) As Double
Dim contingencySize As Double
' previous version with linear velocity
'contingencySize = contingency / averageSliceVelocity(plannedBurnup, startIt, endIt)
'contingencySize = Application.WorksheetFunction.RoundDown(contingencySize, 2)
Dim i As Byte
Dim burnedPoints As Double
Dim remainingContingency As Double
i = endIt
remainingContingency = slice.contingency
' new version based on burned points
Do While remainingContingency > 0
burnedPoints = plannedBurnup.Cells(i) - plannedBurnup.Cells(i - 1)
If remainingContingency > burnedPoints Then
remainingContingency = remainingContingency - burnedPoints
contingencySize = contingencySize + 1
Else
contingencySize = contingencySize + remainingContingency / burnedPoints
remainingContingency = 0
End If
i = i - 1
Loop
getContingencySize = contingencySize
End Function
Function averageSliceVelocity(plannedBurnup As Range, startIt As Byte, endIt As Byte) As Double
If (endIt - startIt) = 0 Then
'if the slice is completed in one iteration the avg velovity is the velocity of that iteration
averageSliceVelocity = (plannedBurnup.Cells(endIt).Value - plannedBurnup.Cells(endIt - 1).Value)
Else
averageSliceVelocity = (plannedBurnup.Cells(endIt).Value - plannedBurnup.Cells(startIt).Value) / (endIt - startIt)
End If
End Function
Function createSlice(sliceRange As Range) As CSlice
Dim slice As CSlice
Set slice = New CSlice
slice.init sliceRange
Set createSlice = slice
End Function
Function getSliceList() As Collection
Dim slices As Collection
Dim sStart, sEnd As Byte
Set slices = New Collection
sStart = getProperty("SLICES_START_COLUMN") + 1
sEnd = getProperty("SLICES_END_COLUMN")
Do While sStart < sEnd
With Worksheets(DATA_TABLE_WS_NAME)
slices.Add createSlice(Union(.Columns(sStart), .Columns(sStart + 1), .Columns(sStart + 2)))
End With
sStart = sStart + 3
Loop
Set getSliceList = slices
End Function
Function getPlannedBurnUp() As Range
Set getPlannedBurnUp = getVisibleDataFrom(Worksheets(PROJECT_WS_NAME).Columns(getProperty("PLANNED_FORECAST_COLUMN")))
End Function
Function getVisibleDataFrom(Column As Range) As Range
Set getVisibleDataFrom = Column.Worksheet.Range(Column.Cells(getProperty("FIRST_DATA_ROW"), 1), _
Column.Cells(getProperty("LAST_DATA_ROW"), 1))
End Function
Function getDataFrom(Column As Range) As Range
Set getDataFrom = Column.Worksheet.Range(Column.Cells(getProperty("FIRST_DATA_ROW"), 1), _
Column.Cells(getProperty("MAX_NUMBER_OF_IT"), 1))
End Function
Sub UpdateDataTableSlices(inWs As Worksheet, outWs As Worksheet)
Dim osStart, osEnd, isStart, isEnd As Byte
Dim iCursor, oCursor As Byte
osStart = getProperty("SLICES_START_COLUMN") + 1
osEnd = getProperty("SLICES_END_COLUMN")
isStart = getProperty("PROJECT_SLICES_START_COLUMN") + 1
isEnd = getProperty("PROJECT_SLICES_END_COLUMN")
If Not (osStart = osEnd) Then
outWs.Columns(osStart).Resize(, osEnd - osStart).Delete
End If
If Not (isStart = isEnd) Then
outWs.Columns(osStart).Resize(, (isEnd - isStart) * 3).Insert
End If
iCursor = isStart
oCursor = osStart
Do While iCursor < isEnd
inWs.Columns(iCursor).Copy
outWs.Columns(oCursor).PasteSpecial
' copy cumulative column from template
Worksheets(TEMPLATE_WS_NAME).Columns(5).Copy
outWs.Columns(oCursor + 1).PasteSpecial
' copy contingency column from template
Worksheets(TEMPLATE_WS_NAME).Columns(6).Copy
outWs.Columns(oCursor + 2).PasteSpecial
oCursor = oCursor + 3
iCursor = iCursor + 1
Loop
'This line is added because when exit from Excel the application ask if you want to save the data in clipboard
Worksheets(TEMPLATE_WS_NAME).Cells(1, 1).Copy
End Sub
CBurnUp Class
Option Explicit
Const CONTINGENCY_RECT_HEIGHT = 6
Const CONTINGENCY_SCALE = 6
Private unitPerIteration As Double
Private unitPerValueY As Double
Private self As Chart
Private originX, originY As Double
Private sliceCounter As Byte
Private mXAxis As Range
Private NUMBER_OF_IT_TO_DISPLAY As Byte
Public Sub init(chartInstance As Chart)
Set self = chartInstance
NUMBER_OF_IT_TO_DISPLAY = getProperty("NUMBER_OF_IT_TO_DISPLAY")
End Sub
Property Let XAxis(xValues As Range)
Set mXAxis = xValues
End Property
Public Sub drawSlice(slice As CSlice)
With self.SeriesCollection.Add(slice.CumulativeSizeRange())
.xValues = mXAxis
.name = slice.name
.Format.Line.ForeColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
End With
sliceCounter = sliceCounter + 1
End Sub
Public Sub drawForecast(forecastRange As Range)
Dim newRange As Range
Set newRange = forecastRange
With self.SeriesCollection.Add(forecastRange)
.xValues = mXAxis
.Format.Line.DashStyle = msoLineDash
.name = "Forecast"
.Format.Line.ForeColor.RGB = RGB(105, 140, 140)
End With
End Sub
Public Sub drawBurnedPoints(burnedPoints As Range)
With self.SeriesCollection.Add(burnedPoints)
.xValues = mXAxis
.name = "BurnedPoints"
.Format.Line.ForeColor.RGB = RGB(233, 91, 80)
End With
End Sub
Public Sub clean()
Dim s As Series
Dim sh As Shape
For Each s In self.SeriesCollection
s.Delete
Next s
For Each sh In self.Shapes
sh.Delete
Next sh
sliceCounter = 0
End Sub
Public Sub drawContingency(forecastedIteration As Byte, scope As Double, contingSize As Double)
If scope = 0 Then
Exit Sub
End If
Dim rectPosX, rectPosY As Double
Dim rectW, rectH As Double
If (contingSize > 0) Then
With self.PlotArea
originY = .InsideTop
originX = .InsideLeft
unitPerIteration = .InsideWidth / NUMBER_OF_IT_TO_DISPLAY
unitPerValueY = .InsideHeight / self.Axes(xlValue).MaximumScale
rectW = getUnitsForItPerc(contingSize)
rectH = CONTINGENCY_RECT_HEIGHT
rectPosX = .InsideLeft + (forecastedIteration * unitPerIteration) - rectW - (unitPerIteration / 2)
rectPosY = .InsideTop + .InsideHeight - (scope * unitPerValueY) - (rectH / 2)
' Create Rect
With self.Shapes.AddShape(msoShapeRectangle, rectPosX, rectPosY, rectW, rectH)
With .Fill
.Visible = True
.ForeColor.RGB = vbYellow
.BackColor.RGB = vbYellow
End With
With .Line
.Visible = True
.ForeColor.RGB = RGB(0, 0, 0)
.BackColor.RGB = RGB(0, 0, 0)
End With
End With
End With
End If
drawLine forecastedIteration
End Sub
Public Sub drawSliceName(slice As CSlice)
Dim txtPosX, txtPosY As Double
Dim rectW, rectH As Double
With self.PlotArea
originY = .InsideTop
originX = .InsideLeft
unitPerIteration = .InsideWidth / NUMBER_OF_IT_TO_DISPLAY
unitPerValueY = .InsideHeight / self.Axes(xlValue).MaximumScale
txtPosX = .width + .InsideLeft
txtPosY = .InsideTop + .InsideHeight - (slice.Cumulative(NUMBER_OF_IT_TO_DISPLAY) * unitPerValueY)
' Create Rect
With self.Shapes.AddTextbox(msoTextOrientationHorizontal, txtPosX, txtPosY, self.ChartArea.width - .width - .Left, 60)
.TextFrame.AutoSize = False
.TextFrame.VerticalAlignment = xlVAlignCenter
.TextFrame.Characters.Font.Size = 14
.TextFrame.Characters.Text = slice.name
.TextFrame.Characters.Font.Color = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
.Left = .Left + .width
.Top = .Top - (.height / 2)
End With
End With
End Sub
Private Sub drawLine(forecastedIteration As Byte)
Dim linePosX, linePosY As Double
With self.PlotArea
unitPerIteration = .InsideWidth / NUMBER_OF_IT_TO_DISPLAY
unitPerValueY = .InsideHeight / self.Axes(xlValue).MaximumScale
linePosX = .InsideLeft + (forecastedIteration * unitPerIteration) - (unitPerIteration / 2)
linePosY = .InsideTop
' Create line
With self.Shapes.AddLine(linePosX, linePosY, linePosX, .InsideHeight + .InsideTop)
.Line.DashStyle = msoLineDash
.Line.Weight = xlThin
.Line.ForeColor.RGB = RGB(150, 150, 150)
End With
End With
End Sub
Private Function getUnitsForItPerc(iterationPercentage As Double)
getUnitsForItPerc = unitPerIteration * iterationPercentage
End Function
Public Sub drawReleaseDate(slice As CSlice, dateRange As Range)
Dim chart_x, chart_y As Double
Dim release_date As Date
Dim width, height As Double
Dim forecasted_iteration As Double
Dim previous_iteration_end_date, next_iteration_end_date As Date
If slice.HasAReleseDate = True Then
release_date = slice.ReleaseDate
If dateRange(1, 1) > release_date Then
Exit Sub
End If
If dateRange(NUMBER_OF_IT_TO_DISPLAY, 1) < release_date Then
Exit Sub
End If
Else
Exit Sub
End If
With self.PlotArea
originY = .InsideTop
originX = .InsideLeft
unitPerIteration = .InsideWidth / NUMBER_OF_IT_TO_DISPLAY
unitPerValueY = .InsideHeight / self.Axes(xlValue).MaximumScale
forecasted_iteration = WorksheetFunction.Match(Int(CDbl(release_date)), dateRange, 1)
previous_iteration_end_date = dateRange(forecasted_iteration, 1).Value
next_iteration_end_date = dateRange(forecasted_iteration + 1, 1).Value
forecasted_iteration = forecasted_iteration + (release_date - previous_iteration_end_date) _
/ (next_iteration_end_date - previous_iteration_end_date)
width = 16
height = 16
chart_x = .InsideLeft + (forecasted_iteration * unitPerIteration) - (unitPerIteration / 2) - (width / 2)
chart_y = .InsideTop + .InsideHeight - ((slice.Cumulative(forecasted_iteration)) * unitPerValueY)
' Create Rect
With self.Shapes.AddShape(msoShapeIsoscelesTriangle, chart_x, chart_y, width, height)
With .Fill
.Visible = True
.ForeColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
.BackColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
End With
With .Line
.Visible = True
.ForeColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
.BackColor.RGB = RGB(slice.Color_Red, slice.Color_Green, slice.Color_Blue)
End With
End With
End With
End Sub
Public Sub drawMilestoneDate(slice As CSlice, dateRange As Range)
Dim chart_x, chart_y As Double
Dim release_date As Date
Dim width, height As Double
Dim forecasted_iteration As Double
Dim previous_iteration_end_date, next_iteration_end_date As Date
If slice.HasAMilestoneDate = True Then
release_date = slice.MilestoneDate
If dateRange(1, 1) > release_date Then
Exit Sub
End If
If dateRange(NUMBER_OF_IT_TO_DISPLAY, 1) < release_date Then
Exit Sub
End If
Else
Exit Sub
End If
With self.PlotArea
originY = .InsideTop
originX = .InsideLeft
unitPerIteration = .InsideWidth / NUMBER_OF_IT_TO_DISPLAY
unitPerValueY = .InsideHeight / self.Axes(xlValue).MaximumScale
forecasted_iteration = WorksheetFunction.Match(Int(CDbl(release_date)), dateRange, 1)
previous_iteration_end_date = dateRange(forecasted_iteration, 1).Value
next_iteration_end_date = dateRange(forecasted_iteration + 1, 1).Value
forecasted_iteration = forecasted_iteration + (release_date - previous_iteration_end_date) _
/ (next_iteration_end_date - previous_iteration_end_date)
width = 16
height = 16
chart_x = .InsideLeft + (forecasted_iteration * unitPerIteration) - (unitPerIteration / 2) - (width / 2)
chart_y = .InsideTop + .InsideHeight - ((slice.Cumulative(forecasted_iteration)) * unitPerValueY) - (height / 2)
' Create Rect
With self.Shapes.AddShape(msoShapeDiamond, chart_x, chart_y, width, height)
With .Fill
.Visible = True
.ForeColor.RGB = RGB(0, 0, 0)
.BackColor.RGB = RGB(0, 0, 0)
End With
With .Line
.Visible = True
.ForeColor.RGB = RGB(0, 0, 0)
.BackColor.RGB = RGB(0, 0, 0)
End With
End With
End With
End Sub
CSlice Class
Option Explicit
Const TPL_THIRD_COLUMN = 3
Const TPL_SECOND_COLUMN = 2
Const TPL_FIRST_COLUMN = 1
Private mSizeCumulative As Range
Private mCumulativeSizeWithContingency As Double
Private mSizeWithPreviousContingency As Double
Private mSize As Range
Private mPlannedSize As Range
Private mName As String
Private mContingency As Double
Private mColor As Range
Private mReleaseDate As Date
Private mMilestoneDate As Date
Private mHasMilestoneDate As Boolean
Private mHasReleaseDate As Boolean
Private mStartingIteration As Byte
Public Sub init(sliceRange As Range)
'Header
mName = sliceRange.Cells(getProperty("SLICE_NAME_ROW"), TPL_FIRST_COLUMN)
mContingency = sliceRange.Cells(getProperty("CONTINGENCY_ROW"), TPL_THIRD_COLUMN)
mCumulativeSizeWithContingency = sliceRange.Cells(getProperty("SIZE_PLUS_CONT_ROW"), TPL_THIRD_COLUMN)
mSizeWithPreviousContingency = sliceRange.Cells(getProperty("PLANNED_SIZE_ROW"), TPL_THIRD_COLUMN)
'Values
Set mPlannedSize = sliceRange.Cells(getProperty("PLANNED_SIZE_ROW"), TPL_FIRST_COLUMN)
Set mSize = getVisibleDataFrom(sliceRange.Columns(TPL_FIRST_COLUMN))
Set mSizeCumulative = getVisibleDataFrom(sliceRange.Columns(TPL_SECOND_COLUMN))
'Dates
mHasReleaseDate = Not (sliceRange.Cells(getProperty("RELEASE_DATE_ROW"), TPL_FIRST_COLUMN) = "")
mReleaseDate = sliceRange.Cells(getProperty("RELEASE_DATE_ROW"), TPL_FIRST_COLUMN)
mHasMilestoneDate = Not (sliceRange.Cells(getProperty("MILESTONE_DATE_ROW"), TPL_FIRST_COLUMN) = "")
mMilestoneDate = sliceRange.Cells(getProperty("MILESTONE_DATE_ROW"), TPL_FIRST_COLUMN)
'Color
Set mColor = sliceRange.Cells(getProperty("SLICE_COLOR_ROW"), TPL_FIRST_COLUMN)
End Sub
Property Get CumulativeWithContingency() As Double
CumulativeWithContingency = mCumulativeSizeWithContingency
End Property
'Current one without contingency plus previous one with cont
Property Get CumulativePreviousContingency() As Double
CumulativePreviousContingency = mSizeWithPreviousContingency
End Property
Property Get Cumulative(ByVal itIndex As Double) As Double
Cumulative = mSizeCumulative(itIndex, 1)
End Property
Public Property Get CumulativeSizeRange() As Range
Set CumulativeSizeRange = mSizeCumulative
End Property
Public Property Get name() As String
name = mName
End Property
Public Property Let PlannedSize(Size As Double)
mPlannedSize.Value = Size
End Property
Public Property Get Color_Red() As Integer
Color_Red = Int(mColor.Interior.Color Mod 256)
End Property
Public Property Get Color_Blue() As Integer
Color_Blue = Int(mColor.Interior.Color / 256 / 256) Mod 256
End Property
Public Property Get Color_Green() As Integer
Color_Green = Int(mColor.Interior.Color / 256) Mod 256
End Property
Public Property Get contingency() As Double
contingency = mContingency
End Property
Public Property Get ReleaseDate() As Date
ReleaseDate = mReleaseDate
End Property
Public Property Get HasAReleseDate() As Boolean
HasAReleseDate = mHasReleaseDate
End Property
Public Property Get MilestoneDate() As Date
MilestoneDate = mMilestoneDate
End Property
Public Property Get HasAMilestoneDate() As Boolean
HasAMilestoneDate = mMilestoneDate
End Property
Public Property Get StartingIteration() As Byte
StartingIteration = mStartingIteration
End Property
Public Property Let StartingIteration(it As Byte)
mStartingIteration = it
End Property
Private Sub Class_Initialize()
End Sub
Project Class
Option Explicit
Private mWS As Worksheet
Public Sub init(projectWs As Worksheet)
Set mWS = projectWs
End Sub
Configuration Module
Option Explicit
Const CONF_WORKSHEET_NAME = "Configuration"
Const KEY_COLUMN = 1
Const VAL_COLUMN = 2
Public Function getProperty(name As String)
Dim confWS As Worksheet
Set confWS = ActiveWorkbook.Worksheets(CONF_WORKSHEET_NAME)
getProperty = WorksheetFunction.Index(confWS.Columns(VAL_COLUMN), WorksheetFunction.Match(name, confWS.Columns(KEY_COLUMN), 0))
End Function