Attach VBA Class Object to Visio shape - vba

Question
I have a shape in visio 2021 , which is the "GRID" found in "Charting Shapes"
I would like to scale the smaller shapes in the master according to the ratios. Therefore I would like to bind a new instance of the class I created below to my master, and then be able to resize the master, which intern would scale the components relative to the ratios.
Code
Class name = LWR_Calc
Private Widths() As Double
Private Heights() As Double
Private W, H As Double
Private TotalWidthRatio, TotalHeightRatio
Private WidthRatioSubDivision, HeightRatioSubDivision
Private Sub Class_Initialize()
W = 1
H = 1
End Sub
Public Sub SetWidths(Lst As String, Optional delimiter As String = ",")
Dim WidthsRatioStrArr() As String
Dim Current As Double
WidthsRatioStrArr = Split(Lst, delimiter)
TotalWidthRatio = 0
ReDim Widths(0 To UBound(WidthsRatioStrArr))
For i = 0 To UBound(WidthsRatioStrArr)
Current = CDbl(WidthsRatioStrArr(i))
Widths(i) = Current
TotalWidthRatio = TotalWidthRatio + Current
Next
WidthRatioSubDivision = W / TotalWidthRatio
End Sub
Public Sub SetHeights(Lst As String, Optional delimiter As String = ",")
Dim HeightsRatioStrArr() As String
Dim Current As Double
HeightsRatioStrArr = Split(Lst, delimiter)
TotalHeightRatio = 0
ReDim Heights(0 To UBound(HeightsRatioStrArr))
For i = 0 To UBound(HeightsRatioStrArr)
Current = CDbl(HeightsRatioStrArr(i))
Heights(i) = Current
TotalHeightRatio = TotalHeightRatio + Current
Next
HeightRatioSubDivision = H / TotalHeightRatio
End Sub
Public Function GetHeight(ByVal index As Integer) As Double
On Error GoTo endr:
GetHeight = Heights(index - 1) * HeightRatioSubDivision
Exit Function
endr:
GetHeight = 0
End Function
Public Function GetWidth(ByVal index As Integer) As Double
On Error GoTo endr:
GetWidth = Widths(index - 1) * WidthRatioSubDivision
Exit Function
endr:
GetWidth = 0
End Function
Public Property Let Width(ByVal vNewValue As Double)
W = vNewValue
End Property
Public Property Let Height(ByVal vNewValue As Double)
H = vNewValue
End Property
my sub which tests the code is as follows
Private Sub Test__LWR_Calc()
Dim LWRC As LWR_Calc
Set LWRC = New LWR_Calc
LWRC.Height = 2
LWRC.Width = 10
LWRC.SetWidths ("1.75,1,1,1,1,1,1,1,1,1")
LWRC.SetHeights ("1.75,1,1,1.75,1,1,1,1,1,1")
For i = 1 To 10
For j = 1 To 10
Debug.Print i & "-" & j & " "; LWRC.GetWidth(j) & " , " & LWRC.GetHeight(i)
Next
Next
Set LWRC = Nothing
End Sub
This code works to get the values below
Data
Output
The Outputs I Get vs the Output I Want.

Related

VBA: Custom data type and function (return value)

I get a compile error at the last line when testing the following: (Only public user defined types that are defined in a public object module can be coerced to or from a variant or passed to late-bound functions.)
Option Explicit
Public Type aType
P_Col As Integer
P_Rad As Single
P_X As Single
P_Y As Single
End Type
Function MakePatterns() As Variant
Dim i As Integer
Dim circles() As aType
For i = 1 To 5
ReDim Preserve circles(i)
circles(i).P_Col = Int(i / 2)
circles(i).P_Rad = i
circles(i).P_X = i * 10 + 1
circles(i).P_Y = i * 10 + 5
Next
For i = 1 To 5
Debug.Print circles(i).P_Col; circles(i).P_Rad; _
circles(i).P_X; circles(i).P_Y
Next
MakePatterns = circles
End Function
Is there a way to use TYPE and Function together to return an array? Or is there a more effective way?
In the code below Sub "TestCallFunction" calls the Function "MakePatterns", and after it prints the first array received back from the function in the Immediate window.
Option Explicit
Public Type aType
P_Col As Integer
P_Rad As Single
P_X As Single
P_Y As Single
End Type
Sub TestCallFunction()
Dim x() As aType
Dim i As Integer
x = MakePatterns
' print the first result received from Function MakePatterns
Debug.Print x(1).P_Col & ";" & x(1).P_Rad & ";" & x(1).P_X & ";" & x(1).P_Y
End Sub
Public Function MakePatterns() As aType()
Dim i As Integer
Dim circles() As aType
For i = 1 To 5
ReDim Preserve circles(i)
circles(i).P_Col = Int(i / 2)
circles(i).P_Rad = i
circles(i).P_X = i * 10 + 1
circles(i).P_Y = i * 10 + 5
Next
For i = 1 To 5
Debug.Print circles(i).P_Col; circles(i).P_Rad; _
circles(i).P_X; circles(i).P_Y
Next
MakePatterns = circles
End Function

Unable to iterate VBA Collection

I am unable to iterate completely through an excel VBA Collection of custom objects. I receive an Invalid procedure call or argument error on the third object.
When examining each object and stepping line-by-line through the procedure, it works on the first two objects, then fails on the third. I've tried executing the code alone, calling the third object instance manually, with no error.
Why would the procedure execute the calculation and set the first two object instance's property correctly, then fail the third time?
I'm new to VBA - what obvious thing am I missing?
The Procedure:
Sub calculateMaintenancePlan()
'Calculates next x maintenance cycles for a number aircraft
'Assumes the spreadsheet defines a named range containing the following columns:
'1 2 3 4 5 6
'Tail Number Current Hours Next Cycle Next Cycle DUE In Heavy Week Id Cycle Start
'6004 11265.0 4 11333.7 FALSE [Integer or -10 if not in heavy]
'debugging/utility variables
Dim temp As Variant
Dim i As Integer, j As Integer
'Assumes the spreadsheet defines a named range containing the following columns:
'Cycle ID Cycle Type Duration Text ID
'1 200 hour 1 HMT1
ReDim mx_cycles(1 To Worksheets("Matrix Inputs").Range("maintenance_cycles").Rows.Count, 1 To 3)
For i = 1 To Worksheets("Matrix Inputs").Range("maintenance_cycles").Rows.Count
mx_cycles(i, 1) = Worksheets("Matrix Inputs").Range("maintenance_cycles").Cells(i, 2)
mx_cycles(i, 2) = CInt(Worksheets("Matrix Inputs").Range("maintenance_cycles").Cells(i, 3))
mx_cycles(i, 3) = Worksheets("Matrix Inputs").Range("maintenance_cycles").Cells(i, 4)
'Debug.Print i & " : " & mx_cycles(i, 1) & " : " & mx_cycles(i, 2) & " : " & mx_cycles(i, 3)
Next i
'an array containing each aircraft tail number, assume spreadsheet
'contains named range called "aircraft"
Dim aircraft As Collection, s_Aircraft As Collection
Dim acft As c_Aircraft
Set aircraft = New Collection
Set s_Aircraft = New Collection
'Set the Collection size to the total number of aircraft on station
'and create a c_Aircraft instance representing each airframe
For i = 1 To Range("aircraft").Count
Set acft = New c_Aircraft
acft.init_aircraft Worksheets("Matrix Inputs").Range("inputs"), i, mx_cycles
aircraft.Add acft, CStr(acft.tailNumber)
Next i
'Sort the aircraft
Set s_Aircraft = sortedAircraft(aircraft)
End Sub
'Sort a Collection of c_aircraft objects
Private Function sortedAircraft(unsortedAircraft As Collection) As Collection
Set sortedAircraft = New Collection
Dim acft As c_Aircraft
Dim temp_acft As c_Aircraft
Dim i As Long, j As Long
Dim next_acft_cycle_start_week_id As Integer
Dim previous_acft As String
Dim t_tailNum
'copy the Collection to a new collection
For Each acft In unsortedAircraft
sortedAircraft.Add acft
Next acft
'Sort the aircraft
For i = 1 To sortedAircraft.Count
For j = i + 1 To sortedAircraft.Count
If sortedAircraft.Item(i).hoursToDUE > sortedAircraft.Item(j).hoursToDUE Then
Set temp_acft = sortedAircraft.Item(j)
sortedAircraft.Remove j
t_tailNum = CStr(temp_acft.tailNumber)
sortedAircraft.Add temp_acft, t_tailNum, i
End If
Next j
Next i
previous_acft = CStr(sortedAircraft.Item(1).tailNumber)
For Each acft In sortedAircraft
If acft.inHeavy = False Then
'******* FAILS here on the third item of six items in the collection
Debug.Print sortedAircraft.Item(previous_acft).weekIdCycleStart + sortedAircraft.Item(previous_acft).nextCycleDuration + 1
previous_acft = CStr(acft.tailNumber)
Else
previous_acft = CStr(acft.tailNumber)
End If
Next acft
End Function
The Object: Represents an aircraft with various properties
Option Explicit
'Class c_Aircraft
'Requires call to init_aircraft() with the Range and "row" of the aircraft instance to be created.
'Assumes the Range contains the following columns:
'1 2 3 4 5 6
'Tail Number Current Hours Next Cycle Next Cycle DUE In Heavy Week Id Cycle Start
'6004 11265.0 4 11333.7 FALSE 0
'Attributes
Private p_tailNumber As Long
Private p_tailNumStr As String
Private p_initialAircraftHours As Double
Private p_currentAircraftHours As Double
Private p_initialNextCycleType As Integer
Private p_nextCycleType As Integer
Private p_initialNextCycleDuration As Integer
Private p_nextCycleDuration As Integer
Private p_initialHoursNextCycleDue As Double
Private p_hoursNextCycleDue As Double
Private p_initialInHeavy As Boolean
Private p_inHeavy As Boolean
'An integer representing the Week Id the current cycle
'started, if in heavy maintenance
Private p_initialWeekIdCycleStart As Integer
Private p_weekIdCycleStart As Integer
Private p_initialHoursToDUE As Double
Private p_hoursToDUE As Double
Private p_initialHoursToDNE As Double
Private p_hoursToDNE As Double
'General Methods
'Custom Initialize
Public Sub init_aircraft(data_range As Range, asset_number As Integer, mxCycles() As Variant)
p_tailNumber = data_range(asset_number, 1)
p_tailNumStr = CStr(data_range(asset_number, 1))
p_initialAircraftHours = data_range(asset_number, 2)
p_currentAircraftHours = p_initialAircraftHours
p_initialNextCycleType = data_range(asset_number, 3)
p_nextCycleType = p_initialNextCycleType
p_initialNextCycleDuration = mxCycles(p_nextCycleType, 2)
p_nextCycleDuration = p_initialNextCycleDuration
p_initialHoursNextCycleDue = data_range(asset_number, 4)
p_hoursNextCycleDue = p_initialHoursNextCycleDue
p_initialInHeavy = data_range(asset_number, 5)
p_inHeavy = p_initialInHeavy
If p_inHeavy Then
p_initialWeekIdCycleStart = data_range(asset_number, 6)
p_weekIdCycleStart = p_initialWeekIdCycleStart
Else
'set to a week prior more than the longest cycle duration
p_initialWeekIdCycleStart = -10
p_weekIdCycleStart = -10
End If
p_initialHoursToDUE = Round(p_hoursNextCycleDue - p_currentAircraftHours, 1)
p_hoursToDUE = p_initialHoursToDUE
p_initialHoursToDNE = Round(p_hoursNextCycleDue - p_currentAircraftHours + 15, 1)
p_hoursToDNE = p_initialHoursToDNE
End Sub
'Return the aircraft objects properties as String
Public Function print_aircraft() As String
print_aircraft = p_tailNumber & vbCrLf & _
"Current Hours: " & p_currentAircraftHours & vbCrLf & _
"Next Cycle: " & p_nextCycleType & vbCrLf & _
"Next Cycle Duration: " & p_nextCycleDuration & vbCrLf & _
"Hours Next Cycle Due: " & p_hoursNextCycleDue & vbCrLf & _
"In Heavy: " & p_inHeavy & vbCrLf & _
"Week Id Cycle Start: " & p_weekIdCycleStart & vbCrLf & _
"DUE: " & p_hoursToDUE & vbCrLf & _
"DNE: " & p_hoursToDNE
End Function
'Get/Let Methods
' Hours Remaining to the DNE
Public Property Get hoursToDNE() As Double
hoursToDNE = p_hoursToDNE
End Property
Public Property Let hoursToDNE(HoursDNE As Double)
p_hoursToDNE = HoursDNE
End Property
'Hours Remaining to the DUE
Public Property Get hoursToDUE() As Double
hoursToDUE = p_hoursToDUE
End Property
Public Property Let hoursToDUE(HoursDUE As Double)
p_hoursToDUE = HoursDUE
End Property
' Aircraft in Heavy Property
Public Property Get inHeavy() As Boolean
inHeavy = p_inHeavy
End Property
Public Sub setInHeavy(Value As Boolean, weekIdCycleStarted As Integer)
p_inHeavy = Value
p_weekIdCycleStart = weekIdCycleStarted
End Sub
'p_weekIdCycleStart
Public Property Get weekIdCycleStart() As Integer
weekIdCycleStart = p_weekIdCycleStart
End Property
'p_weekIdCycleStart
Public Property Let weekIdCycleStart(weekId As Integer)
p_weekIdCycleStart = weekId
End Property
' Aircraft Hours at Next Maintenance Cycle Due Property
Public Property Get hoursNextCycleDue() As Double
hoursNextCycleDue = p_hoursNextCycleDue
End Property
Public Property Let hoursNextCycleDue(Value As Double)
p_hoursNextCycleDue = Value
End Property
' Next Maintenance Cycle Due Property
Public Property Get nextCycleType() As Integer
nextCycleType = p_nextCycleType
End Property
Public Property Let nextCycleType(cycleType As Integer)
p_nextCycleType = cycleType
End Property
' Next Maintenance Cycle Duration Property
Public Property Get nextCycleDuration() As Integer
nextCycleDuration = p_nextCycleDuration
End Property
Public Property Let nextCycleDuration(cycleDuration As Integer)
p_nextCycleDuration = cycleDuration
End Property
' Current Aircraft Hours Property
Public Property Get currentAircraftHours() As Double
currentAircraftHours = p_currentAircraftHours
End Property
Public Property Let currentAircraftHours(Value As Double)
p_currentAircraftHours = Value
End Property
' Tail Number Property
Public Property Get tailNumber() As Long
tailNumber = p_tailNumber
End Property
Public Property Let tailNumber(Value As Long)
p_tailNumber = Value
End Property

Visual basic : Calculate Average function returning infinity

I am supposed to calculate the average number of words and then convert them into a percentage, however they are displaying infinity. The code I am using is below .
Public Class Form1
Private Structure advertisements
Public name As String
Public words As Integer
Public font() As fonts
Public mostLegible As String
End Structure
Private Structure fonts
Public name As String
Public NoWords() As Integer
Public aveWords As Double
Public percent As Double
End Structure
Private noAdverts As Integer
Private noFonts As Integer
Private noReaders As Integer
Private advert() As advertisements
Private Sub GridPlacement(ByVal r As Integer, ByVal c As Integer, ByVal t As String)
grdDisplay.Row = r
grdDisplay.Col = c
grdDisplay.Text = t
End Sub
Private Sub gridAndArraySettings()
Dim x, y As Integer
ReDim advert(noAdverts)
For x = 1 To noAdverts
ReDim advert(x).font(noFonts)
For y = 1 To noFonts
ReDim advert(x).font(y).NoWords(noReaders)
'GridPlacement(0, y, "font " & CStr(y))
Next
Next
grdDisplay.Cols = noFonts + 2
grdDisplay.Rows = noAdverts + 1
GridPlacement(0, 0, "Name of advert")
End Sub
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
noAdverts = CInt(txtadverts.Text)
noReaders = CInt(txtReaders.Text)
noFonts = CInt(txtFonts.Text)
gridAndArraySettings()
End Sub
Private Sub btnInput_Click(sender As Object, e As EventArgs) Handles btnInput.Click
Dim x, y, z As Integer
Dim total(,) As Integer
ReDim total(noAdverts, noFonts)
For x = 1 To noAdverts
advert(x).name = InputBox("please enter the name of advert " & CStr(x))
advert(x).words = CInt(InputBox("please enter the number of words for " & advert(x).name))
GridPlacement(x, 0, advert(x).name)
For y = 1 To noFonts
advert(x).font(y).name = InputBox("please enter the name of font " & CStr(y) & " Used in advert " & CStr(x))
For z = 1 To noReaders
advert(x).font(y).NoWords(z) = CInt(InputBox("please enter the number of words understood by reader " & CStr(z) & " for font " & advert(x).font(y).name & " in advert " & advert(x).name))
total(x, y) += advert(x).font(y).NoWords(z)
Next
advert(x).font(y).aveWords = calcAve(total(x, y))
advert(x).font(y).percent = advert(x).font(y).aveWords * 100
GridPlacement(x, y, CStr(advert(x).font(y).aveWords)) 'Format(advert(x).font(y).percent, "###%")
Next
Next
End Sub
Private Function calcAve(ByVal elements As Integer) As Double
Dim x, y As Integer
Dim placeholder As Double = 1
Select Case elements
Case Is > 0
For x = 1 To noAdverts
For y = 1 To noFonts
advert(x).font(y).aveWords = (elements / noReaders) / advert(x).words
placeholder = advert(x).font(y).aveWords
MsgBox(CStr(placeholder))
Next y
Next x
Case Is = 0
MsgBox("There is incorrect values ")
End Select
Return placeholder
End Function
End Class
The textboxes are called txtadverts, txtfonts and txtreaders
The button btnsubmit gets the input from the textboxes and stores them in variables. btninput allows input from user.
GrdDisplay is a custom extension used by my university which is a 2D grid used to diplay values.

VBA: Initialize object on first use in Module

I kept receiving Object Variable Not Set (Error 91) with the following code in PowerPoint 2013.
I did a step through with this, and it flagged it on the Set SlideCounter = New Counter line which is quite confusing.
Module - Module1:
Dim SlideCounter As Counter
Sub showNext()
If SlideCounter Is Nothing Then
Set SlideCounter = New Counter
End If
ActivePresentation.SlideShowWindow.View.GotoSlide (SlideCounter.GetSlideNumber)
End Sub
Class Module - Counter:
'You should config the following constants
Const kBeginSlide As Integer = 2
Const kEndSlide As Integer = 4
Const kEnddingSlide As Integer = 5
'You should NOT modify anything below
Dim slides As Collection
Private Sub Class_Initialize()
Dim x As Integer
For x = kBeginSlide To kEndSlide
slides.Add (x)
Next x
End Sub
Public Function GetSlideNumber()
If slides.Count = 0 Then
GetSlideNumber = kEnddingSlide
Else
Dim slideIndex As Integer
slideIndex = GetRandomInteger(1, slides.Count)
GetSlideNumber = slides.Item(slideIndex)
slides.Remove (slideIndex)
End If
End Function
Private Function GetRandomInteger(lowerBound As Integer, upperBound As Integer)
Randomize
GetRandomInteger = Int((upperBound - lowerBound + 1) * Rnd + lowerBound)
End Function

This code worked properly the first few times I ran it, now it throws the exception: "System.NullReferenceException"

The first 3 times i ran this code, it worked as I intended. Now whenever I run it, it closes and in the debug screen, it prints: "A first chance exception of type 'System.NullReferenceException' occurred in Battleship - 1 Player.exe". Sorry for the long piece of code, I "collapsed" the properties since they are pretty much the same in every one.
Public Class Enemy
Dim _name As String
Public Property name() As String ...
Dim _length As Integer
Public Property length As Integer ...
Dim _start_point() As Integer
Public Property start_point() As Integer() ...
Dim _space_filled(,) As Integer
Public Property space_filled As Integer(,) ...
Dim _direction As String
Public Property direction() As String ...
Shared gen As New Random()
Public x As Integer = gen.Next(0, 10)
Public y As Integer = gen.Next(0, 10)
Public Sub New(ByVal namep As String, ByVal lengthp As Integer)
name = namep
length = lengthp
ReDim _start_point(2)
ReDim _space_filled(length, 2)
GenerateStartPoint()
GenerateDirection()
ExtendStartPoint()
DefineFilled()
ColorFilled()
End Sub
Public Sub GenerateStartPoint()
start_point = {x, y}
End Sub
Public Sub GenerateDirection()
If gen.Next(0, 2) = 0 Then
direction = "horizontal"
Else
direction = "vertical"
End If
End Sub
Public Sub ExtendStartPoint()
If direction = "horizontal" Then
For i As Integer = 0 To length - 1
space_filled(i, 0) = start_point(0) + i
space_filled(i, 1) = start_point(1)
Next
ElseIf direction = "vertical" Then
For i As Integer = 0 To length - 1
space_filled(i, 0) = start_point(0)
space_filled(i, 1) = start_point(1) + i
Next
End If
End Sub
Public Sub DefineFilled()
For i As Integer = 0 To length - 1
x = space_filled(i, 0)
y = space_filled(i, 1)
Main.TrackerBoard.box_list(x, y).full = True 'Error is coming from here.
Next
End Sub
Private Sub ColorFilled()
For y As Integer = 0 To 9
For x As Integer = 0 To 9
'Debug.Print(Main.PlayerBoard.box_list(x, y).full)
If Main.TrackerBoard.box_list(x, y).full = True Then
Main.TrackerBoard.box_list(x, y).image.BackColor = System.Drawing.Color.Red
Else
Main.TrackerBoard.box_list(x, y).image.BackColor = System.Drawing.Color.Silver
End If
Next
Next
End Sub
End Class
There are several issues with this:
Public Property length As Integer
'...
Public Sub DefineFilled()
For i As Integer = 0 To length - 1
x = space_filled(i, 0)
y = space_filled(i, 1)
Main.TrackerBoard.box_list(x, y).full = True 'Error is coming from here.
Next
End Sub
length is used for loop control in several places, and assuming it is correct. since it is a public property something else anywhere in the app could be changing it. Perhaps you pass it and use it in the constructor as you do to create those arrays, but you really do not need to save it. And you really do not need to make it a public property.
ALL the loops in those procedures (some which themselves should be private) should generally (re) calculate the amount to loop and use a local variable, even if length is private because something else in the class might change it inadvertently.
Private length As Integer
' nothing outside this class needs to call this ever
Private Sub DefineFilled() ...
If it does something more than we can see, it still need not be a property, a private variable is enough.