Using a variable as the name of a macro - vba

This might be a fairly easy "no you can't" answer. But if any of you know of a way to make this work, I would really appreciate it.
I have a macro called "DES" and another called "FA". When I change a cell to either "DES" or "FA" I want it to run either the "DES" or the "FA" macro. I do not what to use a series of if/else logics to figure out the macro to run. I would rather have it so the Go_to_macro() function would feed in the variable and run the function. The reason for this is that I will eventually have dozens of macros that I would like to run this way.
Example code below: "Call command" is where there is a problem.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Range("command_des").Value <> Sheets("Back End").Range("command_be").Value Then
Dim command, number
command = Range("command_des").Value
ticker = Range("number_des").Value
Call Go_to_macro(command, number)
Else
End If
End Sub
//////////////////////////////////////////////////////////////////////////
Public Function Go_to_macro(command, number)
Range("command_be").Value = command
Range("command_" & command).Value = Range("command_be").Value
If Range("number_" & command).Value <> Range("number_be").Value Then
Call command
Else
End If
End Function
//////////////////////////////////////////////////////////////////////////
Sub DES()
Cells(1,1).Value = "Hello World!"
End Sub
/////////////////////////////////////////////////////////////////////////
Sub FA()
Cells(1,1).Value = "Goodbye World..."
End Sub
/////////////////////////////////////////////////////////////////////////
Ideas on a work around?
In the end I should get "Hello World" in cell A1 if I write "DES" into the namedrange or "Goodbye World..." in cell A1 if I write "FA" into the same namedrange. Just so you are clear on the outputs.

You can use
Application.Run command
(FYI, the Call keyword is obsolete and should not be used.
Instead of
Call Go_to_macro(command, number)
use
Go_to_macro command, number
)

Related

Validating Cell Input Based on Another Cell

When I input some text in a cell, for example in cell B2-test, I want in cell A6 the input to begin with this string and to end with _VAR1-for example test_VAR1.
I have found a simple solution as formula - =IF(A2="test","test_VAR1") but I want to make it as a VBA code.
So any idea how this can be done?
This is the most minimal example that I can come up with.
The LCase(Range("B2")) would also take "Test" and "TeSt" into account:
Option Explicit
Public Sub TestMe()
With ActiveSheet
If LCase(.Range("B2")) = "test" Then
.Range("A6") = .Range("B2") & "_VAR1"
End If
End With
End Sub
And if you want to check every event of the worksheet, put your code in the corresponding worksheet (Sheet1, Sheet2 and Sheet3 on the picture below):
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Intersect(Target, Me.Range("B2")) Is Nothing Then Exit Sub
Application.EnableEvents = False
If LCase(Target) = "test" Then
Me.Range("A6") = Target & "_VAR1"
End If
Application.EnableEvents = True
End Sub
I know you said in your question you wanted macro, but I'm not going to post my own (because I feel like #Vityata's answer should be sufficient)
However, I got impression from your post, that you could adjust / improve your formula instead and avoid macro altogether. It's usually better to avoid macro, when possible, for compatibility reasons (a lot of users have macros disabled by default)
If you simply want to add the keyword "_VAR1" to the input, use the following formula instead
=LTRIM(B2) & "_VAR1"
If the input can be anything, that contains the word "test"
=IF(ISNUMBER(SEARCH("test", TRIM(LOWER(B2)))), LTRIM(B2) & "_VAR1", "Incorrect Input")
The text contains only the word "test" and nothing else
=IF(TRIM(LOWER(B2))="test", LTRIM(B2) & "_VAR1", "Incorrect input"
There are some other variations / tricks you could do with this, but these are some of the most basic examples you can use as your "building blocks"

VBA prevent closing of UserForm with "End" in code

In my spreadsheet, I have a UserForm that is supposed to be open at all times.
Once in a while, my code will contain an "End" where I exit the code based on some if statement.
The problem is that this closes the UserForm, is there a way to prevent this from happening?
EDIT:
Sub Test1()
'Random code
Call Test2(Variable)
'Random code
End Sub
Sub Test2(ByVal Variable as Double)
If Variable = 0 then
'Random code
End If
If Variable = 1 then
Call Test3
End 'Original placement of End
End If
End Sub
Sub Test3()
'Random code
End Sub
This is a rough example of how the code is build (its rather long at this point). So depending on the "variable" different things happen in Test2. But if the Variable is 1, then the "random code" back in Test1 can't be executed thus, so I have to stop the code. I tried replace "End" with "Exit Sub" this only stops the code in Test2 from running, is it will give me an error when it goes back to Test1.
EDIT2:
Test1() is actually four different subs (at this point, more will be added) that all call Test2(). That is why I choose to split it up into so many subs and call them from within the subs.
No, not if you insist on using End. This will essentially have the same effect as clicking the "Stop" button in the developer window. You should (most likely) not be using End. I cannot tell you what you should be using, since I do not know what you are trying to achieve.
Update:
Based on your code, I don't see any reason for Test3() to be nested within Test2(), since it runs either the random code or Test3() (never both). Is there anything preventing you from splitting all the different cases into different subs, and then doing the If statement in the main sub?
Sub Main()
If Variable = 0 Then
'Random code from before Test2()
'Random code from Test2()
'Random code from after Test2()
ElseIf Variable = 1 Then
Call Test3()
Else
MsgBox "Variable must be 0 or 1!"
End Sub
You somehow need to tell Test1 that it needs to stop. One approach to this problem is to change your subs to functions and return a value indicating status. Something like this would work:
Function Test1() As Integer
Dim i As Integer
'Random code
i = Test2(Variable)
If i = 1 Then Exit Function
'Random code
End Function
Function Test2(ByVal Variable As Double) As Integer
Test2 = 0
If Variable = 0 Then
'Random code
End If
If Variable = 1 Then
Call Test3
Test2 = 1
Exit Function
End If
End Function
Function Test3() As Integer
'Random code
End Function
End closes anything and kills all the variables and objects that you have.
This is probably the worst way to end any sub and most probably you do not need it.
What's the deference between "end" and "exit sub" in VBA?
https://msdn.microsoft.com/VBA/Language-Reference-VBA/articles/end-statement

Run VBA function with a button

I defined a VBA function that returns a filesize. Now I want to invoke it with a button that's calling a different macro. My expectation is that after running the macro it'll invoke my function at the very end. My problem is that when I put a formula into a cell it will return a current filesize only the moment I enter the formula. When I edit the file, save it and reopen, the =wbksize() will still display the filesize from before my edits.
So the purpose of this macro run by a button is to refresh the filesize value. Here's my attempt to do it.
function:
Function wbksize()
myWbk = Application.ThisWorkbook.FullName
wbksize = FileLen(myWbk)
End Function
refresh:
Worksheets("Sheet2").Range("K1").Calculate
The above doesn't seem to work :/
Function works fine, but refreshing should call function.
Function wbksize() As String
myWbk = Application.ThisWorkbook.FullName
wbksize = Str(FileLen(myWbk))
End Function
Sub Refresh()
Worksheets("Sheet2").Range("K1") = wbksize
End Sub
This may or may not help you in your situation....LINK
I have never needed to use this on excel but it maybe what your looking for, you can set custom functions as 'VOLATILE' which forces excel to run them whenever ANYTHING get calculated, again i have never needed to use this so i cannot comment on any drawbacks or anything but it looks like it may work in your case.
I've tested these, and they both work fine. It depends on what you want your trigger to be: Changing the worksheet, or performing a Calculate on the worksheet.
Put either of these in your Worksheet. The first will trigger on Calculate, the second on Change.
Private Sub Worksheet_Calculate()
Dim lFileLength As Long
Application.EnableEvents = False 'to prevent endless loop
lFileLength = FileLen("\\MyFile\Path\AndName.XLS.XLS")
ThisWorkbook.Sheets("Sheet1").Range("A1").Value = CStr(lFileLength)
MsgBox "You changed THE CELL!"
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lFileLength As Long
Application.EnableEvents = False 'to prevent endless loop
lFileLength = FileLen("\\MyFile\Path\AndName.XLS")
ThisWorkbook.Sheets("Sheet1").Range("B1").Value = CStr(lFileLength)
MsgBox "You changed THE CELL!"
Application.EnableEvents = True
End Sub

Name Manager using VBA - Macro vs. Function Call Gives Different Response

I have an XLA I'm use to make calculations and I'd like to create variables in the Name Manager to use in those calculations. I want to check to see if those named ranged already exist and if not let the user assign values to them. I have a Sub() that I'm using to set the Name Manager -example below- :
Public Sub SetNames()
On Error Resume Next
IsRangeName = CheckName("test")
If IsRangeName = Empty Then
Application.ThisWorkbook.Names.Add Name:="test", RefersTo:=0
End If
End Sub
If I go into the "Macro" menu and run the SetNames routine it works and sets test = 0 in the Name Manager.
However, what I want to do is run this through a Function and allow the function to use the variables in the Name Manager if they exist, if they don't exist then those values get set to an initial value in the Name Manager through the subroutine.
When I try to run the following code the values are never set in the Name Manager:
Sub Function1()
Call SetNames()
-Do Other Things-
End Function
All of the names are declared as global variables.
The intent is to have a user install the add-in and on the first function call using the add-in the Name Manager gets set, either to initialize the names or to allow the user to set the initial value. I don't want the user to go through the Macro ribbon option and execute the subroutine to initialize the Name Manager names.
Any help on this would be appreciated.
This seems to work in my quick testing, but you should be sure it performs in whatever your final use case is. It's a hack around the restrictions on a UDF being able to update the workbook, so it's outside of "normal" usage.
Sub SetNameIfMissing(swb As String)
Dim r As Name, wb As Workbook
Set wb = Workbooks(swb)
On Error Resume Next
Set r = wb.Names("test")
On Error GoTo 0
If r Is Nothing Then
Debug.Print "adding name..."
wb.Names.Add "test", 99
Else
Debug.Print "already added"
End If
End Sub
Function SetIt(v)
Dim wb
wb = Application.Caller.Parent.Parent.Name
'using Evaluate gets around the UDF restriction
Application.Caller.Parent.Evaluate "SetNameIfMissing(""" & wb & """)"
SetIt = "OK" 'or whatever return value is useful...
End Function
Not sure what "CheckName" is in your script - you didn't provide it .. however, I got it to work via:
1) comment out On Error Resume Next - this allows you to see CheckNames failing.
2) Replaced CheckNames with a loop to loop throw the defined names, looking for ours.
3) change your "function" definition from "sub" to "function".
test it, runs fine.
Sets the "test" name if it doesn't exist. Change it manually to another value, run again, doesn't touch it.
Public Sub SetNames()
'On Error Resume Next
For i = 1 To Application.ThisWorkbook.Names.Count
If Application.ThisWorkbook.Names(i).Name = "test" Then
IsRangeName = True
Exit For
End If
Next i
If Not IsRangeName Then
Application.ThisWorkbook.Names.Add Name:="test", RefersTo:=1
End If
End Sub
Function Function1()
Call SetNames
'-Do Other Things-
End Function

Error calling macro from userform

I have a userform to call a macro in a separate module when a button is clicked. I get the following error: "Run-time error '450': Wrong number of arguments or invalid property assignment"
In troubleshooting I removed the arguments and changed the dummy macro I was calling to not take arguments, but I get the same error.
Public Sub btnSubmit_Click()
Dim Description As String
Dim Priority As String
If (checkCleared.Value = False) Then
MsgBox ("Please certify that all sensitive informationhas been removed and then submit")
Exit Sub
Else
'Description = formScreen.txtDesc.Value
'Priority = formScreen.comboPriority.Value
'Application.Run ThisOutlookSession!postScreenedEmail(Priority, Description)
Application.Run ThisOutlookSession!postScreenedEmail
End If
End Sub
In the separate module:
Public Sub postScreenedEmail() '(Priority As String, Description As String)
MsgBox ("postScreened")
'MsgBox ("Priority is: " & Priority & " and Description is " & Description)
End Sub
I have tried other methods of calling the macro such as "Call postScreenedEmail()" but it cant see the macro then. My end goal is just to grab values from the userform and pass them to the other macro so they can be used with the API I am working with.
Edit: I may have mixed my terminology, this is the hierarchy I am working with (can't post pic with my rep). That being said I tried to do the call with just Application.Run "postScreenedEmail", Priority, Description and it changed nothing
-Project1(VbaProject.OTM)
-Microsoft Outlook Objects
| ThisOutlookSession
-Forms
| formScreen
|
-Modules
Module1
Try:
call postScreenedEmail
instead of:
Application.Run ThisOutlookSession!postScreenedEmail
Since your sub is public, vba should be able to find it without the module reference.
If this works, add the reference again (makes your code more readable, especially for others, as ckuhn203 pointed out in the comments) and see if it breaks. If so, that's where the problem is.
EDIT:
Are you sure you're referencing the right module?
If I try:
-Project1(VbaProject.OTM)
-Microsoft Outlook Objects
| ThisOutlookSession
-Modules
| Module1
in Module1:
Sub jzz()
Debug.Print "test"
End Sub
and in ThisOutlookSession:
Sub test()
Call Module1.jzz
End Sub
it works. No problem. Using:
Application.Run Module1.jzz
instead of Call trows a compile error.
Even:
Sub test2()
Call ThisOutlookSession.test
End Sub
from Module1 works, without problems.
Can you run such small tests to try to get the references right?
Try this... Application.Run takes a string for procedure name, and then comma-separated list of parameters/arguments:
Application.Run "Procedure_Name", arg1, arg2, arg3
So I think this should work:
Application.Run "ThisOutlookSession!postScreenedEmail", Priority, Description