use module class in vba - vba

I have a module class file "CombinaisonLine" in my class modules folder :
Private pAdult As String
Private pChild As String
Public Property Get Adult() As String
Adult = pAdult
End Property
Public Property Let Adult(Value As String)
pAdult = Value
End Property
Public Property Get Child() As String
Child = pChild
End Property
Public Property Let Child(Value As String)
pChild = Value
End Property
In my modules folder, I have a function called when I click on a button in my sheet :
Function Test()
Dim Line As CombinaisonLine
If (Sheets("Feuil1").Cells(3, 6).Value = "YES") Then
Line.Adult = "1"
Line.Child = "0"
End If
End Function
I am getting an error 91 at line "Line.Adult="1"" with the following message (I am using a french version, so I have translated the message into english):
execution error "91":
Object variable or Bloc variable With not defined
I don't know what I am missing. Thanks in advance for your help

You need to create object of class CombinaisonLine first, and destroy when you do not need it:
Function Test()
Dim Line As CombinaisonLine
Set Line = New CombinaisonLine
If (Sheets("Feuil1").Cells(3, 6).Value = "YES") Then
Line.Adult = "1"
Line.Child = "0"
End If
Set Line = Nothing
End Function

Related

How do I apply method of the class to the property of the class?

I have a class ClsAnimal containing the string property species, and also method plural which just returns a string with added "s" at the end of a string. I wonder if it's possible to apply .Plural to Animal.Species directly, as shown in the example below:
Sub Test()
Dim Animal As New ClsAnimal
Animal.Species = "cat"
debug.print Animal.Species
'expected result "cat"
debug.print Animal.Species.Plural
'expected result "cats"
End Sub
ClsAnimal Code:
Option Explicit
Private PSpecies As String
Property Let Species(val As String)
PSpecies = val
End Property
Property Get Species() As String
Species = PSpecies
End Property
'returns the name of an animal + "s"
Private Function Plural(val) As String
Plural = val & "s"
End Function
You can kind of hack your way to the behavior you are describing. They way I could implement this is to create a new class that "extends" strings. I've called mine StringExt and it looks like this:
Option Explicit
Private pValue As String
'#DefaultMember
Public Property Get Value() As String
Value = pValue
End Property
Public Property Let Value(val As String)
pValue = val
End Property
Public Function Pluralize() As String
Dim suffix As String
'Examine last letter of the string value...
Select Case LCase(Right(pValue, 1))
Case "" 'empty string
suffix = ""
Case "s" 'words that end in s are pluralized by "es"
suffix = "es"
'Test for any other special cases you want...
Case Else ' default case
suffix = "s"
End Select
Pluralize = pValue & suffix
End Function
This is a wrapper class that wraps around an inner string value. It has a single method which will try to return the plural of the inner string value. One thing to note here is the use of a DefaultMember. I used a really handy vba editor COM addin called RubberDuck to do all the behind-the-scenes work for me with the Default Member. You can do it manually though. You would need to export the class module and modify it in a text editor, adding the Attribute Value.VB_UserMemId = 0 tag inside the property getter:
...
Public Property Get Value() As String
Attribute Value.VB_UserMemId = 0
Value = pValue
End Property
Public Property Let Value(val As String)
pValue = val
End Property
...
Then, import the module back into your vba project. This attribute is not visible in the vba editor. More on default members here but it basically means this property will be returned if no property is specified.
Next, we change up your animal class a bit, using our new StringExt type for the Species property:
Option Explicit
Private pSpecies As StringExt
Public Property Set Species(val As StringExt)
pSpecies = val
End Property
Public Property Get Species() As StringExt
Set Species = pSpecies
End Property
Private Sub Class_Initialize()
Set pSpecies = New StringExt
End Sub
Note here that you'll now need to make sure the pSpecies field gets instantiated since it is an object type now. I do this in the class Initializer to enure it always happens.
Now, your client code should work as expected.
Sub ClientCode()
Dim myAnimal As Animal
Set myAnimal = New Animal
myAnimal.Species = ""
Debug.Print myAnimal.Species.Pluralize
End Sub
Disclamer:
Substituting a basic string type for an object type might cause unexpected behavior in certain fringe situations. You are probably better off just using some global string helper method that takes a string parameter and returns the plural version. But, my implementation will get the behavior you asked for in this question. :-)

VBA compile error: Method or data member not found

everyone. here's my code. When debugging it says "VBA compile error: Method or data member not found" and highlights line: Familienkutsche.strFarbe = "Blau"
If I outcomment it, it says the same thing about the line that follows. What does it not like? Everything is written in one block, so why doesn't he recognize either "strFarbe" or "Geschwindigkeit"? having said that, if I remove Familienkutsche and just leave .strFarbe = "Blau" everything works. Thank you in advance.
Option Explicit
Public strFarbe As String
Private bytTempo As Byte
Private blnTempoSperre As Boolean
Public Property Let Geschwindigkeit(Speed As Long)
If (Speed > 250) Then
bytTempo = 250
blnTempoSperre = True
Else
bytTempo = Speed
blnTempoSperre = False
End If
End Property
Public Property Get Geschwindigkeit() As Long
Geschwindigkeit = bytTempo
End Property
Public Property Get abgeriegelt() As Boolean
abgeriegelt = blnTempoSperre
End Property
Public Sub Autos()
Dim Familienkutsche As Auto
Let Familienkutsche = New Auto
Familienkutsche.strFarbe = "Blau"
Familienkutsche.Geschwindigkeit = 320
Debug.Print Familienkutsche.Geschwindigkeit
Debug.Print Familienkutsche.abgeriegelt
End Sub
The first part of your code must be in a class module Auto.
Public Sub Autos() must be in a standard module. Then it works (with changing Let to Set).
Output:
250
Wahr

Casting from class to interface in Excel VBA

In Excel 2013, I have two classes: LoadCase and LoadCombination, which implement interface ILoadCase.
The declaration for ILoadCase is:
Option Explicit
'' Public properties
Public Property Get Name() As String
End Property
Public Property Let Name(ByVal value As String)
End Property
Public Property Get ID() As Long
End Property
Public Property Let ID(ByVal valus As Long)
End Property
And the (partial) implementations for both LoadCase and LoadCombination are:
Option Explicit
Implements ILoadCase
'' Public properties
Public Property Get ILoadCase_Name() As String
ILoadCase_Name = pName
End Property
Private Property Let ILoadCase_Name(ByVal value As String)
pName = value
End Property
Public Property Get ILoadCase_ID() As Long
ILoadCase_ID = pID
End Property
Private Property Let ILoadCase_ID(ByVal value As Long)
pID = value
End Property
I've omitted code which is irrelevant to the implementation of the interface.
I then have a class BeamForces, which contains results for a particular ILoadCase object:
Option Explicit
Public Fx As Double
Public Fy As Double
Public Fz As Double
Public Mx As Double
Public My As Double
Public Mz As Double
Public ParentLoadCase As ILoadCase
I thought that with this I'd be able to do something like this:
Set currentBeamForces = New BeamForces
With currentBeamForces
.Fx = forces(0)
.Fy = forces(1)
.Fz = forces(2)
.Mx = forces(3)
.My = forces(4)
.Mz = forces(5)
Set .ParentLoadCase = TargetLoadCase
End With
Where TargetLoadCase is either a LoadCase or a LoadCombination, but this gives me an error every time.
I've coded this like I would in .NET and just expected that it would work, but does casting to an interface not work in VBA? Or am I going wrong here somewhere?
EDIT
More details. I first call the following method:
Public Function LoadBeamForcesAtNode(ByVal TargetBeam As Beam, ByVal TargetNode As Node, Optional ByVal TargetLoadCases As Collection = Nothing) As Boolean
Dim i As Integer
Dim currentLoadCase As Variant
Dim targetBeamForces As BeamForces
If TargetLoadCases Is Nothing Then
For Each currentLoadCase In Me.LoadCases.Items
Call TargetLoadCases.Add(currentLoadCase)
Next
For Each currentLoadCase In Me.LoadCombinations.Items
Call TargetLoadCases.Add(currentLoadCase)
Next
End If
'On Error GoTo ExitPoint
For Each currentLoadCase In TargetLoadCases
Set targetBeamForces = InstantiateBeamForces(TargetBeam, TargetNode, currentLoadCase)
If TargetNode Is TargetBeam.Node1 Then
Set TargetBeam.Forces1 = targetBeamForces
Else
Set TargetBeam.Forces2 = targetBeamForces
End If
Next
LoadBeamForcesAtNode = True
ExitPoint:
End Function
Where TargetLoadCases is a collection which can contain both LoadCase and LoadCombination objects.
The problem occurs in InstantiateBeamForces, the code for which is
Private Function InstantiateBeamForces(ByVal TargetBeam As Beam, ByVal TargetNode As Node, ByVal TargetLoadCase As Variant) As BeamForces
Dim forces(5) As Double
Dim currentBeamForces As BeamForces
Call Me.output.GetMemberEndForces(TargetBeam.ID, IIf(TargetNode Is TargetBeam.Node1, 0, 1), TargetLoadCase.ILoadCase_ID, forces, 0)
Set currentBeamForces = New BeamForces
With currentBeamForces
.Fx = forces(0)
.Fy = forces(1)
.Fz = forces(2)
.Mx = forces(3)
.My = forces(4)
.Mz = forces(5)
Set .ParentLoadCase = TargetLoadCase
End With
Set InstantiateBeamForces = currentBeamForces
End Function
Which creates a new BeamForces object and populates it with the values returned by the ...GetMemberEndForces(...) API COM call.
The problem is that the .ParentLoadCase property is nothing after the assignment, so I'm assuming an invalid cast...
** EDIT 2 **
Here is a screenshot of TargetLoadCase when I put a breakpoint in InstantiateBeamForces.
The ILoadCase member is Nothing, but I don't get why. Could this be the cause of the problem?

VBA calling class let method, compile error

Beginner to excel class modules here. I am having trouble with the basics-
When I set (let) the property, I get "Compile error: Wrong number of arguments or invalid property assessment" with the .Name property:
Sub test()
Dim acc As account
Set acc = New account
MsgBox (acc.Name("First Account").rowNum())
End Sub
And this is the "account" class module:
Private strAccName As String
Private mlngRowNum As Long
Public Property Let Name(strN As String)
strAccName = strN
End Property
Public Property Get rowNum(exists As Boolean)
dim rowNum as Long
'...some logic here...
'...
getRowNum = rowNum
End Property
So supposedly I am going wrong in the Let method? Advice greatly appreciated
you can assign a value to a property LET (for normal dataTypes) or property SET (for Object) by the equal sign, not vith parenthesis (used for method instead), or read a property GET assigning the value to another variable, like this:
acc.Name = "xyz"
MsgBox acc.Name
This might help you:
Sub test_class()
Dim acc As account
Set acc = New account
acc.Name = "First Account"
MsgBox acc.rowNum(1)
End Sub
class (account):
Private strAccName As String
Private mlngRowNum As Long
Public Property Let Name(strN As String)
strAccName = strN
End Property
Public Property Get rowNum(exists As Boolean)
'Dim rowNum As Long
'...some logic here...
'...
If exists Then
'getRowNum = rowNum
rowNum = 5
Else
rowNum = 10
End If
End Property

Accessing custom property's value gives 'Out of Memory' error when value is null

I'm trying to create a custom property in an excel sheet, then retrieve its value. This is fine when I don't use an empty string, i.e. "". When I use the empty string, I get this error:
Run-time error '7':
Out of memory
Here's the code I'm using:
Sub proptest()
Dim cprop As CustomProperty
Dim sht As Worksheet
Set sht = ThisWorkbook.Sheets("control")
sht.CustomProperties.Add "path", ""
For Each cprop In ThisWorkbook.Sheets("control").CustomProperties
If cprop.Name = "path" Then
Debug.Print cprop.Value
End If
Next
End Sub
The code fails at Debug.Print cprop.value. Shouldn't I be able to set the property to "" initially?
With vbNullChar it works, sample:
Sub proptest()
Dim sht As Worksheet
Set sht = ThisWorkbook.Sheets("control")
' On Error Resume Next
sht.CustomProperties.Item(1).Delete
' On Error GoTo 0
Dim pathValue As Variant
pathValue = vbNullChar
Dim pathCustomProperty As CustomProperty
Set pathCustomProperty = sht.CustomProperties.Add("path", pathValue)
Dim cprop As CustomProperty
For Each cprop In ThisWorkbook.Sheets("control").CustomProperties
If cprop.Name = "path" Then
Debug.Print cprop.Value
End If
Next
End Sub
I think from the comments and the answer from Daniel Dusek it is clear that this cannot be done. The property should have at least 1 character to be valid, an empty string just isnt allowed and will give an error when the .Value is called.
So you Add this property with a length 1 or more string and you Delete the property again when no actual value is to be assigned to it.
As already mentioned it is not possible to set empty strings.
An easy workaround is to use a magic word or character, such as ~Empty (or whatever seems proof enough for you):
Dim MyProperty As Excel.CustomProperty = ...
Dim PropertyValue As String = If(MyProperty.Value = "~Empty", String.Empty, MyPropertyValue)
A slightly more expensive workaround but 100% safe is to start all the values of your custom properties with a character that you then always strip off. When accessing the value, systematically remove the first character:
Dim MyProperty As Excel.CustomProperty = ...
Dim PropertyValue As String = Strings.Mid(MyProperty.Value, 2)
You can write an extension to make your life easier:
<System.Runtime.CompilerServices.Extension>
Function ValueTrim(MyProperty as Excel.CustomProperty) As String
Return Strings.Mid(MyProperty.Value, 2)
End Function
Now you can use it like this: Dim MyValue As String = MyProperty.ValueTrim
Use a reversed principle when you add a custom property:
<System.Runtime.CompilerServices.Extension>
Function AddTrim(MyProperties As Excel.CustomProperties, Name As String, Value As String) as Excel.CustomProperty
Dim ModifiedValue As String = String.Concat("~", Value) 'Use ~ or whatever character you lie / Note Strig.Concat is the least expensive way to join two strings together.
Dim NewProperty As Excel.CustomProperty = MyProperties.Add(Name, ModifiedValue)
Return NewProperty
End Function
To use like this: MyProperties.AddTrim(Name, Value)
Hope this helps other people who come across the issue..
Based on the other answers and some trial and error, I wrote a class to wrap a Worksheet.CustomProperty.
WorksheetProperty:Class
Sets and Gets the value of a Worksheet.CustomProperty and tests if a Worksheet has the CustomProperty
VERSION 1.0 CLASS
Attribute VB_Name = "WorksheetProperty"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'#Folder("Classes")
'#PredeclaredId
Option Explicit
Private Type TMembers
Name As String
Worksheet As Worksheet
End Type
Private this As TMembers
Public Property Get Create(pWorksheet As Worksheet, pName As String) As WorksheetProperty
With New WorksheetProperty
Set .Worksheet = pWorksheet
.Name = pName
Set Create = .Self
End With
End Property
Public Property Get Self() As WorksheetProperty
Set Self = Me
End Property
Public Property Get Worksheet() As Worksheet
Set Worksheet = this.Worksheet
End Property
Public Property Set Worksheet(ByVal pValue As Worksheet)
Set this.Worksheet = pValue
End Property
Public Property Get Name() As String
Name = this.Name
End Property
Public Property Let Name(ByVal pValue As String)
this.Name = pValue
End Property
Public Property Get Value() As String
Dim P As CustomProperty
For Each P In Worksheet.CustomProperties
If P.Name = Name Then
Value = P.Value
Exit Property
End If
Next
End Property
Public Property Let Value(ByVal pValue As String)
Delete
Worksheet.CustomProperties.Add Name:=Name, Value:=pValue
End Property
Public Property Get hasCustomProperty(pWorksheet As Worksheet, pName As String) As Boolean
Dim P As CustomProperty
For Each P In pWorksheet.CustomProperties
If P.Name = pName Then
hasCustomProperty = True
Exit Property
End If
Next
End Property
Public Sub Delete()
Dim P As CustomProperty
For Each P In Worksheet.CustomProperties
If P.Name = Name Then
P.Delete
Exit For
End If
Next
End Sub
Usage
I have several properties of my custom Unit class return a WorksheetProperty. It makes it really easy to sync my database with my worksheets.
Public Function hasMeta(Ws As Worksheet) As Boolean
hasMeta = WorksheetProperty.hasCustomProperty(Ws, MetaName)
End Function
Public Property Get Id() As WorksheetProperty
Set Id = WorksheetProperty.Create(this.Worksheet, "id")
End Property
Public Property Get CourseID() As WorksheetProperty
Set CourseID = WorksheetProperty.Create(this.Worksheet, "course_id")
End Property
Public Property Get Name() As WorksheetProperty
Set Name = WorksheetProperty.Create(this.Worksheet, "unit_name")
End Property
Simple Usage
'ActiveSheet has a CustomProperty
Debug.Print WorksheetProperty.hasCustomProperty(ActiveSheet, "LastDateSynced")
'Set a CustomProperty
WorksheetProperty.Create(ActiveSheet, "LastDateSynced").Value = Now
'Retrieve a CustomProperty
Debug.Print WorksheetProperty.Create(ActiveSheet, "LastDateSynced").Value