Declare a "type" object globally - vba

I want to pass objects between form modules.
I have made a simple database to explain the problem I am having.
I have two forms --> form1, form2 and one module --> Module1
Based on the threads I have read, I believe if I want to define a global object, it should be done in the module so I placed the declaration there;
Option Compare Database
Public Type Name
First As String
Last As String
Phone As String
End Type
On form1 I have a cmd button with the following code behind it;
Option Compare Database
Dim My_Name As Name
Private Sub cmd_Button_Click()
My_Name.First = "MyFirstName"
My_Name.Last = "MyLastName"
MsgBox ("In Form1 " & My_Name.Last)
DoCmd.OpenForm "Form2"
End Sub
on the second form I have;
Option Compare Database
Dim my_Name As Name
Private Sub Form_Load()
MsgBox (my_Name.First)
End Sub
In the first form, I see the my_Name object variable fine, when I pass control to the second form, I lose the variable.
I have tried a number of variations of Public, Static declarations all over the place on both the object variable and the sub's but I cannot figure out how to preserve the object across modules.

You are re-declaring my_name. So it is in -effect two different things. Like george forman and george forman.
I am sorry I can't be more help it's been like a a million years since I have had to deal with this. However, it seems that your 'froms' are actually declared in an 'module' like block -name from1. That from global scope you can reference a variable in your form1.
In this case form1.my_name. However, this can lead to issues if form1.my_name has not yet been instanciated.
My recommendation is to find the module with global scope ( I dont remember exactly what it is, if you have to create it or now, and when it gets life). Declare and initicialize your variables in it. Then access those from within you event handlers ("cmd_Button_Click" for example.)

Related

VBA Calling a private function inside of a userform

I have a userform defined as "SingleListForm".
I have a private sub named "loadSingleListForm(ID As Double)"
I am attempting to pass a variable into the form and I was implementing it in the fashion that loadSingleListForm would set the form up based on ID, basically re-using one form to show variable data from variable sources in the listbox.
But calling a standard Intersect from outside the form (Worksheet_SelectionChange) these two options compile but do not work.
Application.Run "SingleListForm.loadSingleListForm", ID 'ID already declared and assigned
This doesn't work either
Call ActiveWorkbook.UserForm("SingleListForm").loadSingleListForm(ID)
Where it says UserForm I have also tried SingleListForm.
Here the runtime error is:
I am trying hard not to use a Global Variable here to pass to the form.
Perhaps I should go to Initialize and try something there.
I am trying to pass the variable to the form and then of course set up the form based on this case and then show the form. you can't pass with show so you have to find another way to set up.
I just realized I have not called a userform private function from outside of the form before, but I do it with modules all the time. The first case works in that instance.
Cheers,
-WWC
The better way to do this is to declare a property to the form. In the form's module enter
Option Explicit
Private myID as double
Property Set ID(i as double)
myID = i
End Property
Then your function
Private Sub loadSingleListForm()
can refer to myID with in it's code
To Use this from outside modules you use
Load SingleListForm
SingleListForm.ID = ID 'ID variable already declared
Declare your sub in the form as Public Public Sub loadSingleListForm(ID As Double) and then call it like this SingleListForm.loadSingleListForm ID
Just to cover this. Empty workbook, one button.
The button calls to a private function in the form that does nothing but open a message box. Testing concept here.
This is all there is:
Doesn't work:
UserForm1.you_made_it
Error at compile, method or data member not found
Same if this:
With ThisWorkbook
UserForm1.you_made_it
End With
Then try this:
Application.Run "UserForm1.you_made_it"
Error: Cannot run macro . . . .
Try this from first comment:
ActiveWorkbook.UserForm("UserForm1").you_made_it
Error: Object doesn't support this property or method
So this is the winner from above. Not sure I wanted to go Public but it works.
Doesn't solve how to use a private member in the form but it gets the coding going forward.
Public Sub you_made_it()
MsgBox ("you made it")
End Sub
So far:
1) Move the private to a module and then call it
2) make the function Public and it works
Thank you,
-WWC

Passing References through forms vb.net

Is there anyone with very good knowledge in vb.net. I am working on my final semester project. I need to pass objects between forms. I have my codes of two forms here.
http://pastebin.com/xP1LdL3t
http://pastebin.com/fpuY98NT
To connect to the irc server i am using irc.Connect() function. It is perfectly working on my channel form and it is to be called only when users want to connect or on form load. When i double click the online user list a private message form opens. And i am unable to send irc.sendraw command and that form has not called irc.Connect(). It is not supposed to be called in every form. What i want is to use the channel's connection status on other forms so that irc.sendraw command will work.
If i have failed to explain properly please let me know.
It's not clear what you mean by "pass references". There are several ways to communicate between forms. Here are a few:
Declare public variables or properties in one form to be accessed by other forms. If you do this, be sure you are using the proper instance of the form containing the public variables. If you refer to one of these variables before that form is loaded, you'll end up with two instances of the form and things will get confusing.
Use a public method, similar to (1)
Declare global variables in a separate module, to be accessed by any form in the project. (Some people consider this bad manners.)
Pass parameters to and from the forms.
Raise an event to be handled in another form.
basically,
If you want to pass through function in Form 1 to function in Form 2 you can do somthing like this:
Public Class Form1
Dim x As Integer = 2
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Form2.fun(x)
Form2.Show() ' it's optional
End Sub
End Class
and in Form 2 you just get the value as ref:
Public Class Form2
Dim y As Integer 'the variable to get the value of x
Public Sub fun(ByRef x As Integer)
y = x
End Sub
End Class
Hope it can help you, and it's what you wanted.

Public variables are not REALLY public in VBA in Forms

Below is a question that I will answer myself, however it caused a GREAT deal of frustration for me and I had a lot of trouble searching for it on the web, so I am posting here in hopes of saving some time & effort for others, and maybe for myself if I forget this in the future:
For VBA (in my case, MS Excel), the Public declaration is supposed to make the variable (or function) globally accessible by other functions or subroutines in that module, as well as in any other module.
Turns out this is not true, in the case of Forms, and I suspect also in Sheets, but I haven't verified the latter.
In short, the following will NOT create a public, accessible variable when created in a Form, and will therefore crash, saying that the bYesNo and dRate variables are undefined in mModule1:
(inside fMyForm)
Public bYesNo As Boolean`
Public dRate As Double
Private Sub SetVals()
bYesNo = Me.cbShouldIHaveADrink.value
dRate = CDec(Me.tbHowManyPerHour.value)
End Sub
(Presume the textbox & checkbox are defined in the form)
(inside mModule1)
Private Sub PrintVals()
Debug.Print CStr(bYesNo)
Debug.Print CStr(dRate)
End Sub
However, if you make the slight alteration below, it all will work fine:
(inside fMyForm)
Private Sub SetVals()
bYesNo = Me.cbShouldIHaveADrink.value
dRate = CDec(Me.tbHowManyPerHour.value)
End Sub
(Presume the textbox & checkbox are defined in the form)
(inside mModule1)
Public bYesNo As Boolean`
Public dRate As Double
Private Sub PrintVals()
Debug.Print CStr(bYesNo)
Debug.Print CStr(dRate)
End Sub
mModule1 will work perfectly fine and, assuming that the fMyForm is always called first, then by the time the PrintVals routine is run, the values from the textbox and checkbox in the form will properly be captured.
I honestly cannot possibly fathom what MS was thinking with this change, but the lack of consistency is a huge suck on efficiency, learning idiosyncracies like these, which are so poorly documented that a Google search in 2013 for something that has likely been around for a decade or more is so challenging to search.
First comment:
Userform and Sheet modules are Object modules: they don't behave the same way as a regular module. You can however refer to a variable in a userform in a similar way to how you'd refer to a class property. In your example referring to fMyForm.bYesNo would work fine. If you'd not declared bYesNo as Public it wouldn't be visible to code outside of the form, so when you make it Public it really is different from non-Public. – Tim Williams Apr 11 '13 at 21:39
is actually a correct answer...
As a quick add-on answer to the community answer, just for a heads-up:
When you instantiate your forms, you can use the form object itself, or you can create a new instance of the form object by using New and putting it in a variable. The latter method is cleaner IMO, since this makes the usage less singleton-ish.
However, when in your userform you Call Unload(Me), all public members will be wiped clean. So, if your code goes like this:
Dim oForm as frmWhatever
Set oForm = New frmWhatever
Call oForm.Show(vbModal)
If Not oForm.bCancelled Then ' <- poof - bCancelled is wiped clean at this point
The solution I use to prevent this, and it is a nice alternative solution for the OP as well, is to capture all IO with the form (i.e. all public members) into a separate class, and use an instance of that class to communicate with the form. So, e.g.
Dim oFormResult As CWhateverResult
Set oFormResult = New CWhateverResult
Dim oForm as frmWhatever
Set oForm = New frmWhatever
Call oForm.Initialize(oFormResult)
Call oForm.Show(vbModal)
If Not oFormResult.bCancelled Then ' <- safe
There are other limitations to Public within Excel VBA.
MSoft documentation in learn.microsoft.com states that public variables are global to the VBA project - it's not true.
Public variables are only global to the workbook within which they are declared, and then only across standard modules. Public variables declared within workbook code are not visible in standard modules, even though standard module sub's are - which are defined to be public.
Public variables declared in one workbook's standard modules are certainly not accessible from other workbooks in the same VBA project, contrary to the MSoft documentation.

How to prevent VBA variables from being shared across Word documents?

I have a VBA template project that runs automatically when a Word document is opened. However, if I open multiple documents, they all share the variables values. How can declare these variables to be only associated with the active window or active document?
I tried declaring them in a Class Module, but that did not help. Switching between opened document I can see that these variables are shared.
Any input is appreciated...
This what I have in my Module:
Option Private Module
Dim CurrentCommand As String
Public Function SetCurrentCommand(command)
CurrentCommand = command
End Function
Public Function GetCurrentCommand()
GetCurrentCommand = CurrentCommand
End Function
More Info: The code/Macro start at AutoExec like this:
Public Sub Main()
Set oAppClass.oApp = Word.Application
If PollingRate <> "" Then Application.OnTime Now + TimeValue(PollingRate), "CaptureUserViewState"
End Sub
And the CaptureUserViewState is a Sub that resides in a different Module and does all teh checks (comparing new values to last recorded ones) and here how this Sub does the check:
If WL_GetterAndSetter.GetLastPageVerticalPercentage <> pageVerticalPercentScrolled Then
'Update the last value variable
WL_GetterAndSetter.SetLastPageVerticalPercentage (pageVerticalPercentScrolled)
'log change
End If
You don't give us much information, but I assume you declared public variables at module level like this:
Public myString As String
Public myDouble As Double
From VBA documentation:
Variables declared using the Public statement are available to all procedures in all modules in all applications unless Option Private Module is in effect; in which case, the variables are public only within the project in which they reside.
The answer is to use Option Private Module.
When used in host applications that allow references across multiple projects, Option Private Module prevents a module’s contents from being referenced outside its project.
[...] If used, the Option Private statement must appear at module level, before any procedures.
EDIT You have now clarified that you declare your variables using Dim at module level. In this case, Option Private Module is irrelevant.
Variables declared with Dim at the module level are available to all procedures within the module.
i.e. regardless of whether you're using Option Private Module or not.
If you're finding that the values are retained between runs, then that must be because you are running a procedure from the same module from the same workbook. You may think you're doing something else, but in reality this is what you're doing.
EDIT
In your class module, instead of Dim CurrentCommand As String try Private CurrentCommand As String. Without more information it's hard to debug your program. I'm just taking random potshots here.
What you need to do is store multiple versions of the variables, one set per document.
So I would suggest that you create a simple class to hold the different values.
You then store them in a collection mapping the data-set with the document name or similar as the key.
In classmodule (MyData), marked as public:
Public data1 as String
Public data2 as Integer
In module with the event-handlers:
Dim c as new Collection 'module global declaration
Sub AddData()
Dim d as new MyData 'Your data set
d.data1 = "Some value"
d.data2 = 42
c.add Value:=d, Key:=ActiveDocument.name
End Sub
Then when you enter the event-handler you retrieve the data and use the specific set for the currently active document.
Sub EventHandler()
Dim d as MyData
set d = c.item(ActiveDocument.name)
'use data
'd.data1...
End Sub
Please not that this code is just on conceptual level. It is not working, You have to apply it to your problem but it should give you some idea on what you need to do. You will need to add alot of error handling, checking if the item is already in the collection and so on, but I hope you understand the concept to continue trying on your own.
The reason for this is because, as I understand the situation from your question, you only have one version of your script running, but multiple documents. Hence the script have to know about all the different documents.
On the other hand, If each document would have their own code/eventhandlers, hence having multiple versions of the script running, then you don't need the solution provided above. Instead you need to be careful what document instance you reference in your script. By always using "ThisDocument" instead of "ActiveDocument" you could achieve isolation if the code is placed in each open document.
However, as I understood it, you only have one version of the script running, separate from the open documents, hence the first solution applies.
Best of luck!
You might want to store the Document Specific details using
The Document.CustomDocumentProperties Property
http://msdn.microsoft.com/en-us/library/office/aa212718(v=office.11).aspx
This returns a
DocumentProperties Collection
Which you can add new Properties to Using
Document.CustomDocumentProperties.Add(PropertyName, LinkToContent, Value, Type)
And then Read From using
Document.CustomDocumentProperties.Item(PropertyName)
A downside, or bonus, here is that the properties will remain stored in the document unless you delete them.
This may be a good thing or a bad thing

Best practice for sharing variables across forms in VB.NET

I need to share variables across two forms in VB.NET. One of them is the main form and the other is a child form.
I have been searching, and I have found a few methods. I want to use the best method to do this. I have listed a few options below. Please comment on which one would be the best option:
Create a static/shared variable in one of the forms and access it in the other forms via:
Form1 frm = new Form1(); // Creating object of parent to access shared variable
frm.a = "abc"; // Passing value
Send an instance of the main form to the child form when creating the child form. The variables can then be accessed via a property function.
Create global variables in a module. This seems like the easiest option, but I doubt it is the best option.
I also read something about delegates and events, but I don't know how to implement this.
If there is another method I haven't mentioned, please share it with me.
There is no one answer to the question. All the methods you listed should 'work.' Which you should use depends why you want to share the variable. For example:
Say you have a form with a list of records, and the user double-clicks a record, so you want to open a new form to edit the record, and you want to pass the record ID. In this case I would add a constructor method to the second form:
Sub New(RecordID as String)
'Add code to load the record here
End Sub
Say some of the forms in your application may want to know the database path or something else global like that. For that, I would make the appropriate variable on the parent form into a Public variable (called a Field) and access it as MainForm.FieldName. (Disclaimer: Purists will say you shouldn't rely on the somewhat messy fact that VB.NET automatically instantiates an instance of the form class and lets you refer to it by the form name, and that you should instead get a pointer to the actual instance of the form and store it in your child form and access the parent form like that. Actually, this is like number '2' in your post. But it's not actually necessary if you don't mind programmatical incorrectness.)
Say there is something global in your app, like the time the app was started, so you can tell the user "You've been using the app for 5 hours, go get a life!" These things could be stored in a module. (Or in the application class but that's quite hidden)
Create two forms. Add 3 radio buttons and 1 button to form1. Add a label to form2. In the code for form1 type
Public rdb As Integer = 1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Form2.Show()
If RadioButton1.Checked Then
rdb = 1
ElseIf RadioButton2.Checked Then
rdb = 2
ElseIf RadioButton3.Checked Then
rdb = 3
End If
End Sub
Then in form2's code
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = Form1.rdb
End Sub
Store your global variables in a Module1.vb file, they must be publicly declared to be accessible from all forms:
Public X as String
Public Y as Integer
Then just use them as you would any other variable on any page:
X = "Hello"
Y = 10
Textbox1.Text = X
Textbox2.Text = Y
It's not the safest practice so it shouldn't be used for all your variables. But it is very neat and simple.
The child form can have public get and set functions for a private variable, and the parent form can set it when it changes and its end, or get it before it uses it to see if it has changed in the child form.
You can add public properties to either form. They can access those properties from each other. (That is not called shared though, and is not static in most cases.)
Not sure if this answers the question but one thing I found useful was to refer to the variables in form1 while programming in form2 as Form1.variablename and when in Form1 and referring to variables in Form2 to use in Form1 as Form2.variablename Basically, refer to variables in other forms by putting the name of the form they are in followed by a . and then the variable name