Type of variable scoping in vba language - vb.net

I try to understand the VBA scope type, it's impossible to make this such of thing in VBA, but it's possible in other language (java,scala,etc):
public sub try()
dim myVar as String
myvar = "hello world"
Call displayVar()
end sub
public sub displayVar()
msgbox (myvar)
end sub
Can you give me some information about this type of limited scoping ? It's dynamical or lexical, i don't really understand the difference :/

Franck Leveque has provided a clear and simple demonstratation of the difference between local and global declarations.
However, like most languages, VBA allows you to pass parameters to subroutines. Sometimes a global variable is the only choice or the only sensible choice. But normally it it is better to declare myVar within try and pass it as a parameter to displayVar. This prevents displayVar from accidentally changing myVar because, by default, parameters are passed as values. If you want a subroutine to change the value of a parameter you must explicitly pass the parameter as a reference. This is true of most modern programming languages.
Note also that Public means these subroutines are visible to subroutines in other modules. If Public was omitted or replaced by Private, try and displayVar would only be visible within their module.
In the code below I have passed the value of myVar as a parameter to displayVar.
Public Sub try()
Dim myVar As String
myvar = "hello world"
Call displayVar(myVar)
End Sub
Public Sub displayVar(Stg As String)
Call Msgbox(Stg, VBOKOnly)
End Sub

Variable myVar is declared in the function try()
As such, you can only use it in the try() function scope.
By try() function scope, I mean all the instruction written between public sub try() and end sub.
You try to call your variable from another function (displayVar). Which define it's own scope and is not inside the try function scope.
If you want this, you have to declare your variable in the global scope (outside any function)
For example :
dim myVar as String
public sub try()
myvar = "hello world"
Call displayVar()
end sub
public sub displayVar()
msgbox (myvar)
end sub

Related

How to access the Format function in a ComboBox derived class?

I am writing a Combobox custom control and cannot use the Format() function. The editor marks the format line as an error.
Public Class TestCombo
Inherits ComboBox
Protected Overrides Sub OnDrawItem(ByVal e As DrawItemEventArgs)
Dim MyStr = Format(5459.4, "##,##0.00") ' The error is here.
End Sub
End Class
After a little exploring, I found that Combobox uses ListControl as an event.
How can I tell the editor to address the function and not the event?
As you noticed, the ComboBox class has an event called Format (which is inherited from ListControl). So, when you try to call the Format() function inside ComboBox, the compiler thinks that you are trying to use the event because it has the narrowest scope, hence, the error.
To get around this, you may explicitly call the module name (i.e., Strings) where the Format() function is declared:
Dim MyStr = Strings.Format(5459.4, "##,##0.00")
Alternatively, you may use String.Format() or ToString() (which is the standard way in .NET):
Dim MyStr2 = String.Format("{0:##,##0.00}", 5459.4)
Dim MyStr3 = 5459.4.ToString("##,##0.00")

Calling Variables

Can I call variables from outside the Public Sub which was defined in in Visual Basic?
This is rather basic-level stuff, but if you don't know what to search for, it can be hard to know how to find the information. This should get you started.
You don't "call" variables, you "call" procedures. A variable that's declared with the Dim keyword inside a procedure scope, is local to that scope it's declared in, so no.
This is best observed with Option Explicit specified.
Option Explicit
Public Sub Test1()
Dim foo As Long
End Sub
Public Sub Test2()
foo = 42 ' illegal: variable is not declared / not accessible in this scope
End Sub
The concept to understand here, is scoping. Use Dim to declare local variables. As the name says, such variables only exist in the scope they're declared in.
Next you have module scope. You can use the Dim keyword outside of a procedure scope, at the top of the module for this, but for consistency it's probably a better idea to use the Private keyword.
Option Explicit
Private foo As Long
Public Sub Test1()
foo = 42
End Sub
Public Sub Test2()
MsgBox foo
End Sub
This code will compile, and if Test1 is invoked before Test2, the Test2 call will pop 42 in a message box.
Then you have public scope, aka "global". You can use the obsolete Global keyword for those, but for consistency it's better to use the Public keyword.
Option Explicit
Public foo As Long
Public Sub Test1()
foo = 42
End Sub
Option Explicit
Public Sub Test2()
MsgBox foo
End Sub
The above will do exactly the same as the previous snippet, except now we have the two procedures in separate modules (standard modules - it's important). And the code will compile and run.
Rule of thumb, you don't need to declare global variables.
Variables should always be scoped as tightly as possible, and passed around as parameters. Parameters can be passed by value (ByVal) or, by reference (ByRef). If unspecified, ByRef is the [unfortunate] default.
Option Explicit
Public Sub Test()
Dim foo As Long
Assign foo
MsgBox foo
End Sub
Private Sub Assign(ByVal bar As Long)
bar = 42
End Sub
Running Test will pop a message box saying 0, because ByVal passes a copy of the value (or a copy of the pointer to the object reference, when we're talking about objects).
Contrast with:
Option Explicit
Public Sub Test()
Dim foo As Long
Assign foo
MsgBox foo
End Sub
Private Sub Assign(ByRef bar As Long) ' or implicit: (bar As Long)
bar = 42
End Sub
This will pop a message box saying 42, because ByRef passes a pointer to the value (or in the case of object references, the pointer itself). Note that this is usually not behavior you want to allow, hence most parameters should be passed by value.

VBA Encryption Method

I have written a very simple Ceaser Cipher encryption algorithm on VBA. It takes a string value and applies x shift.
Quite happy with it. However, would like to take it to the next step, but I am not sure if it is doable.
I would like to call this encryption function in another module and pass a string value to encrypt.
I.e. What I am trying to do is something like this
Private Sub Encryption()
'Encryption method of string Var
End Sub
---- and in another sub ----
Private Sub function()
Dim Text as String
Text = "Hello"
Encryption(Text)
End Sub
So in this example, I have defined a method to encrypt any string variable. In the other function, I defined a string and initialised it as "Hello". Then called Encryption function on it... Not sure if this is doable in VBA?
I am not sure how I could pass in a variable when I am calling a function within a function. Any advise please?
You would need to change the Sub Routine to a Function. Also, get rid of the Private keyword as this will not allow you to call it across different modules.
So Change this:
Private Sub Encryption()
'Encryption method of string Var
End Sub
to this:
Function Encryption(ByVal inputString as String) As String
'Encryption method of string Var
End Sub
Now, inside your function you need to change whatever variable holds the first string (the one to be encrypted) to the newly created variable, inputString. It's hard to help you in this area as you did not provide the full code for Sub Encryption().
Also, ensure that you set the Function name itself to the newly encrypted text within the function. So your function would essentially look as follows:
Function Encryption(ByVal inputString as String) As String
'Encryption method of string Var
Encryption = 'the value to return from this function
End Sub
Now, your second sub would look like this:
Private Sub test()
Dim Text as String, encryptedText as String
Text = "Hello"
encryptedText = Encryption(Text)
End Sub
You will just make the new variable encryptedText equal to the new function, Encryption.
So, the major difference between a Sub and a Function is that a Function will return values. They both essentially process code the same way.
METHOD 2 (Edit)
After rethinking your question, I believe that you were using your Sub to obtain your encrypted text from passing the variable as ByRef, and you may just be having difficulties with calling the Sub because of the Private keyword.
If this what was going on, then you can simply remove the Private keyword and it should work as intended.
So your Sub should look like:
Sub Encryption() '<-- Notice, no Private scope
'Encryption method of string Var
End Sub
or
Public Sub Encryption()
'Encryption method of string Var
End Sub
You should take a look at this for additional reading regarding scope.

Global Variables not working

I have a problem with global variable
I'm working with modules in vba
-- In module5 is define the global variable
Public tes As Integer
-- In module6 I define the "tes"
Function a()
tes = 1
End Function
-- In module I try to call "tes" variable
Sub test()
MsgBox tes
End Sub
I suppose the result should show 1, but it show 0
I wonder what's wrong
Thanks
Randy
If a never runs, tes is never assigned. Make sure a runs before you read tes and the msgbox will show 1:
Sub test()
a
MsgBox tes
End Sub
That said, it's probably better to pass tes as a parameter instead of sprinkling global variables that can be modified from anywhere.
Sub DoSomething()
Dim foo As Integer
foo = 1
Test foo
End Sub
Sub Test(ByVal value As Integer)
MsgBox value
End Sub
Variables should usually be as tightly scoped and short-lived as possible; use globals only when you need them (that should be rarely), not everytime a function/procedure needs to access some data.

To dimension an incoming variable or to not

What is the difference between doing this:
Public Sub MySub(ByVal MyString as String)
Dim TheString as String = MyString
myFunction(TheString)
'Some more code...
End Sub
Compared to this:
Public Sub MySub(ByVal MyString as String)
MyFunction(MyString)
'Some more code
End Sub
That is, is there a good reason to dimension a variable in a function/sub again before using it from the argument line? Or does it not matter?
Hope this makes sense, I wasn't sure how to word this question.
If it's a value based Type (e.g: string or integer, double etc.), unless it's passed using the reference (ByRef) instead of the value (ByVal), you shouldn't need to worry about storing it in another variable (unless you wish to have 2 copies)
If the function required a modified string, so you were changing it to fit that, the first code block would make some sense.
However--since it's equal, the only case I can think of you needing the first block of code is if you're needing to keep myString the same within that sub...but I would think if you were doing something like that, most of the time you would just have a totally different sub.
Because you used ByVal-- once you exit that sub, myString will once again be whatever it was initially set to before you entered the sub, because the mystring you're working with inside of the sub is just a copy of the value.
In contrast, if you used ByRef the changes made to the variable would be taken back to the main code and actually change the value of mystring.
You can read about the difference between ByVal and ByRef here: http://social.msdn.microsoft.com/Forums/vstudio/en-US/07b9d3b9-5658-49ed-9218-005564e8209e/what-is-the-difference-between-byval-and-byref
You can take all these concepts and play with how it works in a simple console program to further your understanding. That's what I did, here's some of the code I toyed with to double check what I was saying. Try changing where ByVal & ByRef is used, and maybe printing to the screen after you make a change to see what sticks and what doesn't.
Sub Main()
Dim someString As String = "This is some string."
MySub(someString)
Console.WriteLine(someString)
MyOtherSub(someString)
Console.WriteLine(someString)
Console.ReadLine()
End Sub
Public Sub MySub(ByVal myString As String)
Dim theString As String = myString
MyFunction(theString)
' First line printed in console.
Console.WriteLine(theString)
theString = "Hello."
Console.WriteLine(theString)
Console.WriteLine(myString)
myString = "I'm mystring."
Console.WriteLine(myString)
End Sub
Public Sub MyOtherSub(ByVal MyString As String)
MyFunction(MyString)
End Sub
Public Function MyFunction(ByRef mystring As String)
mystring += "myFunction is acting on this string."
End Function