excel VBA return result of function - excel

Simple Layup for anyone. I've written a function isMac() which looks at the current operating System, if the first 3 letters are Mac, it is supposed to return True. I then want to assign the return value of the function to a variable in another function. I'm currently running a Mac, so when I run the following code, the MsgBox says True, while the Debug.Print returns False
Function isMac() As Boolean
If Left(Application.OperatingSystem, 3) = "Mac" Then
result = True
Else
result = False
End If
MsgBox result
End Function
Sub test()
Debug.Print isMac()
End Sub
How do I correctly return a Boolean from my IF statement so that I can utilize it in another function?

Try assigning the result to the function.
Function isMac() As Boolean
isMac = CBool(LCase(Left(Application.OperatingSystem, 3)) = "mac")
End Function
Sub test()
Debug.Print isMac()
End Sub
Another approach would be Compiler Directives and Compiler Constants. In a module code sheet's declarations area as,
#If Mac Then
Public Const bMAC as Boolean = True
#Else
Public Const bMAC as Boolean = False
#End If
Use bMAC anywhere in your code to determine whether the OS is a Mac or not.

Related

How to set global variables in VBA

I would like to define a global variable in VBA in one module and use this in other VBA modules.
I am trying to follow: How do I declare a global variable in VBA?
I have created a new module called "GlobalVariables", and first declared the Public variables and then set their value within a function (trying to do this in open code causes an error). My code is below.
But the Global variable StartYear does not seem to be available into other VBA modules. What am I doing wrong?
Option Explicit
Public StartYear As Integer
Public BaseYear As Integer
Function DeclareGlobalVariables()
StartYear = ActiveWorkbook.Worksheets("RunModel").Range("StartYear").Value
BaseYear = ActiveWorkbook.Worksheets("RunModel").Range("BaseYear").Value
End Function
Make sure you put your golobal variable in a module and not in worksheet scope, to make it accessible in other modules.
Your Function should be a Sub because it does not return anything.
Your code will error if your cells eg. contain text (strings). Never trust a user's input. Always validate!
So I suggest the following
Module 1
Option Explicit
Public StartYear As Long
Public BaseYear As Long
Public Function InitializeGlobalVariables() As Boolean
InitializeGlobalVariables = True
With ActiveWorkbook.Worksheets("RunModel").Range("StartYear")
If IsYear(.Value) Then
StartYear = CLng(.Value)
Else
InitializeGlobalVariables = False
MsgBox "StartYear needs to be a number"
End If
End With
With ActiveWorkbook.Worksheets("RunModel").Range("BaseYear")
If IsYear(.Value) Then
BaseYear = CLng(.Value)
Else
InitializeGlobalVariables = False
MsgBox "BaseYear needs to be a number"
End If
End With
End Function
'validate if the input value is a valid year
Private Function IsYear(ByVal InputValue As Variant) As Boolean
If IsNumeric(InputValue) Then
If CLng(InputValue) = InputValue And _
InputValue > 0 And InputValue < 9999 Then 'integer not decimal AND 4 digit year
IsYear = True
End If
End If
End Function
And you can access the variables in any other module like:
Module 2
Option Explicit
Public Sub TestOutput()
'before using the variables test if they are initialized (not 0)
If StartYear = 0 Or BaseYear = 0 Then
'they are not initalized so initalize them (and at the same time check if it was successful)
If InitializeGlobalVariables = False Then
'the function returns FALSE if the initialization process failed so we need to cancel this procedure or we use not initilized variables!
MsgBox "Variables were not intitialized. Trying to initialize them failed too. I cannot proceed."
Exit Sub
End If
End If
Debug.Print StartYear
Debug.Print BaseYear
End Sub

Set VBA Variable to Result from Function which is String

I've read a bunch about functions today and they all seem to deal with math/numbers. I'm trying to use a function which returns a string and capture that as a variable in the "main sub" but I can't get it to work. Can anybody point out what I'm doing wrong?
Eg:
Function:
Public Function Test(var) As Variant
Bar = var & "World"
MsgBox Bar
End Function
Sub:
Public Bar As Variant
Public Z As Variant
Sub testing()
Call Test("Hello") ' This displays "HelloWorld" from MsgBox in Function
Test ("Hello") ' This displays "HelloWorld" from MsgBox in Function
Z = Test("Hello") ' This displays "HelloWorld" from MsgBox in Function
MsgBox Z ' This displays an empty MsgBox :*(
End Sub
If you want the function to return a value, populate the function variable and return that in the main sub, like so
Public Function Test(var As String) As String
Test = var & " World"
End Function
Sub testing()
returnstr = Test("Hello")
MsgBox returnstr
End Sub
You aren't returning a value from the function. Also, functions should only be used to return values, not to perform actions that change things (other than variables) or display pop-ups. It also gets confusing having global variables and passing variables to a function. You generally pass local variables to a function. Here's a cleaner example (with your main function first, as is normal convention):
Sub Testing()
Dim Z As String
Z = Test("Hello")
MsgBox Z
MsgBox Test("Hello")
End Sub
Public Function Test(ByRef var As String) As String
Test = var & "World"
End Function

How not to reload an excel Addin

I have built an Excel Macro (as an Excel Addin) to randomly generate numbers.
I built it because the rand() function on excel keep generating new numbers at each action in the Excel file. So I tried to build something that "freeze" the formula once it has generated a number.
It works pretty well but when I close the file and reopen it, the numbers change.
How can I fix that ?
I have tried something like : If current cell = Blank ==> generate, otherwise exit function. But it doesn't work.
Here is the code I'm using :
Function RandomFreeze()
Static AlreadyRandomized As Boolean
AlreadyRandomized = False
Static Low As Double
Static High As Double
Low = 1
High = 100000000
If Worksheets("Sheet1").Range("A1") = "" Then
If AlreadyRandomized = False Then
RandomFreeze = Int(Rnd * (High + 1 - Low)) + Low
AlreadyRandomized = True
End If
Else
MsgBox "Erreur"
AlreadyRandomized = True
End If
End Function
Any help with this issue will be appreciated.
Thanks in advance
You can store the value of AlreadyRandomized variable to a CustomDocumentProperty and read/set its value accordingly.
Public Sub T()
Dim p As Object
Set p = CustomPropertyByName("AlreadyRandomized")
If Not CBool(p.Value) Then 'Randomize
p.Value = True 'Randomized
End Sub
Two helper functions, one creates the property if it doesn't exist and returns a reference to it and the second simply checks if the property exists.
'CustomPropertyByName()
Public Function CustomPropertyByName(ByVal propertyName As String) As Object
If Not PropertyExists(propertyName) Then
ThisWorkbook.CustomDocumentProperties.Add Name:=propertyName, _
LinkToContent:=False, _
Type:=msoPropertyTypeNumber, _
Value:=0
End If
Set CustomPropertyByName = ThisWorkbook.CustomDocumentProperties(propertyName)
End Function
'PropertyExists()
Private Function PropertyExists(ByVal propertyName As String) As Boolean
Dim p As Object
For Each p In ThisWorkbook.CustomDocumentProperties
If p.Name = propertyName Then
PropertyExists = True
Exit Function
End If
Next p
End Function
Note: A runtime error occurs if you try to access a CustomDocumentProperty that doesn't exist.

Type mismatch when trying to assign return value to variable (VBA Excel)

Long time listener, first time caller.
I am having an issue with VBA in Excel 2010. I am trying to compare various fields on a user form to determine if they are empty. I will then highlight red, and set focus on the first one on the list.
To do this, I have created functions for each type that can be blank/not selected. They are all declared AS BOOLEAN. The idea was to do the function call to highlight, pass the function calls with the appropriate sub functions, and use the results of those in the highlight function:
function Blah(DoThis01(Me.Object1), DoThis02(Me.Object2), ...) As Boolean
That gave me nojoy; the values always came back false. So I created three boolean variables to assign the values of those functions, and it started givinng me byRef errors. After declaring each variable rather than all on one line, that solved the byref, but now I get a type mismatch:
Dim foo As Boolean
foo = DoThis01(Me.Object1)
if Blah(foo, bar, ..)
It doesn't like that center line; that is where I am running into the error.
Below is the code snippet I am working with, along with an example of the functions I am trying to pass. I can't figure out why my As Boolean functions aren't compatible with an As Boolean variable. Also, the functions to determine whether or not the fields are filled are giving me some issues as well, not registering the correct values. Google had lots of answers, but none that worked for me so far. I appreciate any insight you can offer.
Thanks,
J
PS I realize some of the logic may be a bit off when triggering the message. That is ok, because I am not getting the right values anyways. I will clean it up if I can get the rest working.
Working Code:
Save Button:
Dim emptyRow As Long
Dim isDuplicate As Boolean, nameSelect As Boolean, comboSelect As Boolean, listSelect As Boolean
Dim listLength As Integer
Dim blah As Integer
' check for empty on form
nameSelect = IsSelectedName(Me.SignalNameTxtBox)
comboSelect = IsSelectedCombo(Me.ComboBox1)
listSelect = IsSelectedList(Me.ListBox1)
If HighlightEmpty(nameSelect, comboSelect, listSelect) Then
blah = MsgBox("Empty fields required!", vbOKOnly)
Exit Sub
End If
...More stuff
To Highlight Empty Form Objects:
Function HighlightEmpty(nameSelect As Boolean, comboSelect As Boolean, listSelect As Boolean) As Boolean
' Highlight empty fields
If Not nameSelect Then Enter_New_Form.SignalNameTxtBox.BackColor = RGB(255, 0, 0)
If Not comboSelect Then Enter_New_Form.ComboBox1.BackColor = RGB(255, 0, 0)
If Not listSelect Then Enter_New_Form.ListBox1.BackColor = RGB(255, 0, 0)
' Set focus to first empty field on form
If Not nameSelect Then
Enter_New_Form.SignalNameTxtBox.SetFocus
ElseIf Not comboSelect Then
Enter_New_Form.ComboBox1.SetFocus
ElseIf Not listSelect Then
Enter_New_Form.ListBox1.SetFocus
End If
' Return boolean to trigger message
HighlightEmpty = nameSelect Or comboSelect Or Not listSelect
End Function
My functions to determine if empty:
Function IsSelectedList(lst As ListBox) As Boolean
Dim I As Integer
For I = 0 To lst.ListCount - 1
IsSelectedList = lst.Selected(I)
If IsSelectedList Then Exit Function
Next I
End Function
Function IsSelectedCombo(cbo As ComboBox) As Boolean
If cbo.Value = "" Then IsSelectedCombo = False
End Function
Function IsSelectedName(nme As TextBox) As Boolean
If nme.Value = "" Then IsSelectedName = False
End Function
Your functions will always return false unless otherwise declared as true. Add an else statement like this:
Function IsSelectedName(nme As TextBox) As Boolean
If nme.Value = "" Then
IsSelectedName = False
Else
IsSelectedName = True
End If
End Function
As #Timwilliams pointed out you can reduce this to one line since nme.Value = "" will evaluate to True or False. You might find however, that you need to test if the cell is empty in other ways such as isBlank() and isEmpty(). In this case use the previous example.
Function IsSelectedName(nme As TextBox) As Boolean
IsSelectedName = Len(nme.Value) > 0
End Function
EDIT
To check the value just pass the text string to the function rather than the textbox object. Try This:
Private Sub CommandButton1_Click()
MsgBox (IsSelectedName(Me.SignalNameTxtBox.Text))
End Sub
Function IsSelectedName(nme As String) As Boolean
IsSelectedName = Len(nme) > 0
End Function

VBA Excel: not statement & formula

Is there a way of combining a not statement with a function in the following way:
I want it to work like this example
MsgBox not False
which returns True.
But if I add a function instead of the boolean statement
MsgBox not myFunction()
then it returns a mismatch error.
myFunction() is supposed to return True or False
Any suggestions?
You will need to give us more code, as the following works:
Public Sub test()
MsgBox Not myfunction()
End Sub
Function myfunction() As Boolean
myfunction = False
End Function
Simply
MsgBox Not Application.Evaluate(myFunction)
what is myFunction defined as? if its not :
Function myFunction(...) As Boolean
'...
End Function
that would be why, 'not' operator is reserved for Boolean (true/false)
if your function is like this and only recieves 1 and 0:
Function myFunction(...) As Integer
add this to the call :
MsgBox not CBool(myFunction(...))
or if you are actually trying to compare text:
Function myFunction(...) As String
then you will need:
EDIT: not string.compare, (StrComp(Str1, Str2, vbTextCompare) = 0)
hope that helps :)

Resources