If you want to insert a variable Form2, I use this ..
Dim Variable As New Form2
But if I have the same functions with names in two forms, I'll do it using if.
if 1 = 1 Then
Dim Variable As New Form2
Else
Dim Variable As New Form3
End If
That's perfectly fine, but if I start using this variable in the code below, an error occurs if I use without conditions, everything is fine, use when the condition seemed to understand what to do.
Variable.DataGridView1.Rows.Add(row)
Object reference not set to instance of an object
You need to declare your variable first and then assign it to a particular type:
Dim Variable As Form = Nothing
If 1 = 1 Then
Variable = New Form2()
Else
Variable = New Form3()
End If
But usually you would do this in an object oriented approach by each form implementing a common interface.
You can then do this:
Dim Variable As IForm
If 1 = 1 Then
Variable = New Form2() 'Form2 implements IForm
Else
Variable = New Form3() 'Form3 implements IForm
End If
Related
Right now I have an module where I call an object with the "write" method.
I do this 500 places around in the code, and now I need to support 2 objects instead of only 1 - But I will only write to one of them depending on an variable state
So originally I would have to write
If var = 1 Then
Module1.Obj1.Write("some_data")
Else If var = 0 Then
Module1.Obj2.Write("some_other_data")
End If
How do I create an object that can redirect the data based on an variable so I can write like:
Module1.ObjX.Write("data_here")
And down in the ObjX it reads "var" and if var = 1 then writes to Obj1 or if var = 0 write to Obj2
'if var = 1 then
Module1.Obj1.Write("data")
else if var = 0 then
Module1.Obj2.Write("data")
It would make the code easier to understand and save a lot of work :S
A standard way of doing what you want is to create a dictionary of key value pairs. The key is the value that selects which object you need to use to write and the value associated with the Key is the object you will use. The code below uses a scripting.dictionary to achieve this.
At the module Level
Public myWriter as Scripting.Dictionary
In an initialisation subroutine
Set myWriter = New Scripting.Dictionary
with myWriter
.Add 1,Obj1
.Add 0.Obj2
.Add 3.Obj3
' etc
End With
To call the approprite write function you can now just use
myWriter.Item(var).Write(DataValue)
'
Pass the object by reference into a write function instead:
Sub WriteData(ByRef obj, ByVal data)
'Write the data using the object here
End Sub
WriteData(Obj1, "data")
WriteData(Obj2, "data")
Change the value of writervar before you call write object
Since writervar is global you can edit the value anywhere in your code.
Public writervar as Integer ' global scope
Public Sub WriteObj(Data)
Select Case writervar
Case 0
' do stuff
Case 1
' do stuff
Case Else
' error
End Select
End Sub
I am trying to find a way for class variables to persist between modules.
I have a use class that stores typical user data: Name, domain, manager, etc.
I'd like to store this information throughout the life of the session (while the user is using the tool), but it looks like I can't do that. Below is an example and thank for your help/advice!
JP
Here is the class module:
Private cLoggedDomain As String
Private cLoggedRole As String
Private cDepartment As String
Private cEmployeeName As String
Private cManagerName As String
Private cEmp_ID As Long
Private cEmployeeInfo As Collection
Public Property Let SetUser(value As String)
'RECIEVES THE LOGGED DOMAIN AS STRING
'GETS THE DB ATTRIBUTES FROM SQL
Set cEmployeeInfo = GetInfoFromSearch("Employee, manager, department, ety_type, emp_ID", _
"domainID = '" & value & "'", _
"Employee", "v_roster_empViewALL")
cLoggedDomain = value
cEmployeeName = cEmployeeInfo(1)(1)
cManagerName = cEmployeeInfo(1)(2)
cDepartment = cEmployeeInfo(1)(3)
cLoggedRole = cEmployeeInfo(1)(4)
cEmp_ID = cEmployeeInfo(1)(5)
End Property
Public Property Get LoggedDomain() As String
LoggedDomain = cLoggedDomain
End Property
Public Property Let LoggedDomain(value As String)
cLoggedDomain = value
End Property
Public Property Get LoggedRole() As String
LoggedRole = cLoggedRole
End Property
Public Property Get LoggedDepartment() As String
LoggedDepartment = cDepartment
End Property
Public Property Get LoggedEmployeeName() As String
LoggedEmployeeName = cEmployeeName
End Property
Public Property Get LoggedManagerName() As String
LoggedManagerName = cManagerName
End Property
Public Property Get LoggedEmpId() As String
LoggedEmpId = cEmp_ID
End Property
And the module that uses it, which works fine:
Public Sub New_LoadMain()
Dim s As Worksheet
Dim loggedUser As New cRoles
'CHECK TO SEE IF USER IS LOGGED IN
If loggedUser.LoggedDomain = "" Then
'Set loggedUser = New cRoles
loggedUser.SetUser = Environ("username")
Else
End If
Call test
However, when I try to use the test module, I get a with block error?
Sub test()
Dim test As cRoles
Dim t As String
t = test.LoggedDepartment
End Sub
Class modules define the public interface for objects: they are blueprints that mean nothing until they are instantiated with the New keyword.
When you do this:
Dim test As cRoles
You allocate memory for an object pointer, and telling the compiler that this object implements the cRoles interface; that's how you can type test. and get a list of all the public members on that interface.
However that object pointer points to no object: it's Nothing (literally). You need to create a new instance of that class in order to access the object test is pointing to:
Set test = New cRoles
And now accessing test members will no longer throw error 91.
Now, each instance encapsulates its own state: think of each worksheet in your workbook as a Worksheet instance: each sheet has its own separate content, but all sheets can be manipulated through the same Worksheet interface, regardless of whether you're looking at Sheet1 or Sheet42.
The same is true for all instances of your cRoles class:
Dim test1 As cRoles
Set test1 = New cRoles
test1.SetUser = user1
Dim test2 As cRoles
Set test2 = New cRoles
test2.SetUser = user2
Debug.Print test1.LoggedEmpId, test2.LoggedEmpId
The two instances are completely distinct, and each hold their own internal state. If that's what you want, then in order to create an instance in one place and consume it in another place, you'll need to pass the object reference as a parameter:
Public Sub Test()
Dim thing As cRoles
Set thing = New cRoles
thing.SetUser = Environ("username")
DoSomething thing
End Sub
Private Sub DoSomething(ByVal auth As cRoles)
Debug.Print auth.LoggedEmpId
End Sub
Note:
You typically want to pass parameters ByVal
Avoid As New since that makes an auto-instantiated object, and that comes with behavior that may or may not be expected.
You could have a global-scope Public AuthInfo As cRoles variable declared in a standard module, then a procedure responsible for creating the object and setting this global-scope reference. Then you can access AuthInfo everywhere in your VBA project - the caveat being, that global variable can now be written to by any code in your VBA project. Prefer using local variables and parameters if possible.
I'm writing a program that has two forms. One form gets the user to enter multiple values, and then does some calculations. Then it passes that information to another form However I can't figure out how to do it. Here is a relevant part of my code. To head some confusion, I am trying to pass 11 values, also initially, form 2 is not shown, and then when the values are passed from form 1 to form 2, then form 1 goes away and form 2 is the only one that shown
NOTE: This is not all my code, I don't believe all my code is required (I have 1000 lines right now) However this is the code with the information I want to be passed to the other form.
A lot of people are apparently saying that this is a duplicate of another question, however that question, he seems to already know how to pass the variables, but is just having issues with it (and even with looking at his, i cant figure it out)
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
'declarations
Dim intNormal As Integer
Dim intChildren As Integer
Dim intBonanza As Integer
Dim intDiamond As Integer
Dim intPictureFrame As Integer
Dim intKite As Integer
Dim intCrazyT As Integer
Dim intLetterX As Integer
Dim int2PostageStamp As Integer
Dim intPick7 As Integer
Dim intJackpot As Integer
Validate()
If txtNormal1.Enabled = False Then
intNormal = intNormInput
Else
intNormal = CalcNormalBooks()
End If
If txtChildren1.Enabled = False Then
intChildren = intChildInput
Else
intChildren = calcChildrensBooks()
End If
If txtBonanza1.Enabled = False Then
intBonanza = intBonInput
Else
intBonanza = calcBonanza()
End If
If txtSpecial1.Enabled = False Then
intSpecial = intSpeInput
Else
intSpecial = calcSpecialBooks(intSpecial)
End If
If txtDiamond1.Enabled = False Then
intDiamond = intDiaInput
Else
intDiamond = calcDiamond(intSpecial)
End If
If txtPictureFrame1.Enabled = False Then
intPictureFrame = intPicInput
Else
intPictureFrame = calcPictureFrame(intSpecial)
End If
If txtKite1.Enabled = False Then
intKite = intKiteInput
Else
intKite = calcKite(intSpecial)
End If
If txtCrazyT1.Enabled = False Then
intCrazyT = intCrazyInput
Else
intCrazyT = calcCrazyT(intSpecial)
End If
If txtLetterX1.Enabled = False Then
intLetterX = intLettInput
Else
intLetterX = calcLetterX(intSpecial)
End If
If txt2PostageStamp1.Enabled = False Then
int2PostageStamp = intPostInput
Else
int2PostageStamp = CalcPostageStamp(intSpecial)
End If
If txtPick71.Enabled = False Then
intPick7 = intPickInput
Else
intPick7 = calcPick7(intSpecial)
End If
If txtJackpot1.Enabled = False Then
intJackpot = intJackInput
Else
intJackpot = calcJackpot()
End If
End Sub
Since I had almost the same requiremnt lately here is my solution:
Custom Event which fires when your 2nd Form is closing
Public Event HotKeyFormClosed As EventHandler(Of HotKeyFormClosedEventArgs)
Custom EventArgs class where you store your values you want to pass to Main Form
Public Class HotKeyFormClosedEventArgs
Inherits EventArgs
'Your properties here
Public Sub New(...) 'your params here
MyBase.New()
'set your properties here
End Sub
End Class
On 2nd Form handle FormClosed event and pass your values to EventArgs
Private Sub HotKey_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs)
RaiseEvent HotKeyFormClosed(Me, New HotKeyFormClosedEventArgs(...)) 'your params here
End Sub
On Main Form handle your custom event (here HotKeyFormClosed) and extract its values
AddHandler frmHotKey.HotKeyFormClosed, AddressOf HotKey_FormClosed;
...
Private Sub HotKey_FormClosed(sender As Object, e As HotKeyFormClosedEventArgs)
'Do stuff with values from e
End If
I have chosen the Event approach since it decouples the two forms from another.
One could easily duplicate the information on both forms, make them public and access it directly thru an object instance.
But I like the observable approach from the events more due to it gives mor flexibility (additonal forms using the same events etc.)
P.S.: I wrote my code in c# and blind entered the VB code here so be gracious.
The values/variables that a method expects to receive (specified in the method's signature) are called Parameters.
The values sent to a method when the method is called are called Arguments.
As long as the arguments used when calling a method match the parameters for that method, those values can be passed.
For example (and I'll try to apply this to your context), if you want to create an instance of a form that takes certain values, you can specify those parameters in the form's New event, like so:
Public Sub New(someInt As Integer)
'do something with someInt here
End Sub
Then when you call this method you'd pass it the arguments, like so:
Dim myInt As Integer = 10
Dim newForm As myForm = New myForm(myInt)
When I say the arguments need to match the parameters, that means the number of values, the order of those values, and the value types must be the same (or in the case of numbers the parameter's type must be the same or larger than the argument's type).
As long as that is true, then it shouldn't really matter how you pass these - you could pass 11 individual arguments, you just have to make sure you are matching the argument to the parameter.
Hope that helps!
I have this code here:
Dim MasterIndex As String()()
Private Function Lookup(ByVal Search_path As String) As Integer
Dim i As Integer = 0
Do Until MasterIndex(i)(0) Is Nothing
If Search_path = MasterIndex(i)(0) Then
Return MasterIndex(i)(1)
End If
Loop
Return -1
End Function
Which gives me the error Object reference not set to an instance of an object occuring on the Do Until line. Why is this? How can I fix this?
The MasterIndex variable is never assigned that is why you have the exception
You should instantiate MasterIndex first by calling the New() constructor:
Dim MasterIndex As new String()()
and fill it with data before calling the Lookup function.
Something like:
Private MasterIndex As String()() = New String()() {New String() {"A1", "A2"}, New String() {"B1", "B2"}}
Either MasterIndex is not initialized or MasterIndex(0) is not initialized.
Can you show the code that initializes that variable, assuming you do that somewhere else in the program?
What happens if you put a breakpoint on that line and examine MasterIndex?
The one calls form 2 as a dialog, and passed by ref a "pointer" to the base class(abstract).
//Form 1 calling form two. And Passing a ref object
Dim CreateForm As New frmCreate(Robot)
//Second Forms Overloaded New
Public Sub New(ByRef Robot As cRobot)
InitializeComponent()
thisRobot = Robot
End Sub
Select Case (cbType.SelectedIndex)
Case 0
lblOther.Text = "Bullet Proof Value"
Dim SecRobot = New cSecurityRobot
SecRobot.Name = txtName.Text
SecRobot.Temperature = nudTemp.Value
SecRobot.Threshold = nudThreshold.Value
SecRobot.BulletproofValue = nudOther.Value
thisRobot = SecRobot
Case 1
lblOther.Text = "Special Moves"
Dim SpRobot = New cSportsRobot
SpRobot.Name = txtName.Text
SpRobot.Temperature = nudTemp.Value
SpRobot.Threshold = nudThreshold.Value
SpRobot.SpecialMoves = nudOther.Value
thisRobot = SpRobot
Case 2
lblOther.Text = "Domestic Skills"
Dim SerRobot = New cServiceRobot
lblOther.Text = "Domestic Skills"
SerRobot.Name = txtName.Text
SerRobot.Temperature = nudTemp.Value
SerRobot.Threshold = nudThreshold.Value
SerRobot.DomesticSkills = nudOther.Value
thisRobot = SerRobot
Case Else
lblOther.Text = "Bullet Proof Value"
Dim SecRobot = New cSecurityRobot
SecRobot.Name = txtName.Text
SecRobot.Temperature = nudTemp.Value
SecRobot.Threshold = nudThreshold.Value
SecRobot.BulletproofValue = nudOther.Value
thisRobot = SecRobot
End Select
Form 2 assigns some values and terminates, but there is always a NULL Exception occurring
Let's take a look at your constructor:
Public Sub New(ByRef Robot As cRobot)
InitializeComponent()
thisRobot = Robot '<-- Problem is here
End Sub
On the line indicated above, you are making a copy of the reference, and so the ByRef no longer helps you.
Thinking about how to get around this problem, you might be able to do it by nesting the Robot inside another class:
Public Class RobotContainer
Public Property Robot As Robot
End Class
Pass a RobotContainer instance to your constructor in the normal (ByVal) way and keep a reference to that entire object in your class. Now, both your frmCreate type and the calling code have a reference to the same object. When you update the Robot property on that object, it will be updated for both locations.
But really, this whole design doesn't smell right. Normally I would suggest a method that return the created robot, rather than trying to assign it to an outside location directly, but I understand that working with Windows Forms controls this may not be option. To suggest a better solution we'd need to see a lot more of your code.
Hmm... looking back at this I wanted to do something to make the RobotContainer more useful:
Public Class ReferenceContainer(Of T)
Public Property Item As T
End Class
No, the "ByRef"-ness is only relevant for the method in which the parameter is declared. The value of the thisRobot variable is still just a reference value. Changing the value of that variable later on won't change the caller's variable.