Use data from one userform to formula in another - vba

Quite new to vba and have been trying to figure out how to use data from my first userform to my second userform.
let's call them userform1 and userform2
So in userform1, user will enter data for a, b, c ,and d. Upon clicking OK, userform2 will open:
Private Sub OK_Click()
l = cdbl(a.value)+cdbl(b.value)
w = cdbl(c.value)+cdbl(d.value)
userform1.hide
userform2.show
End Sub
In userform2, i need the values of a and b (entered by user in userform1) to compute for x and y:
Private Sub OK_Click()
x = cdbl(a.value)+cdbl(d.value)
y = cdbl(b.value)+cdbl(c.value)
End Sub
tried placing a placing a b c d l w x and y in a module and setting in Public but code still doesn't work. Error "object required"
Thanks very much in advance.

A simple maybe not the best way is to declare variables a and b in the class modules. The better way might be to pass them via properties.
Code in Userform1
Option Explicit
Public a As Double
Public b As Double
Private Sub CommandButton1_Click()
a = TextBox1.Value
b = TextBox2.Value
Me.Hide
End Sub
Code in Userform 2
Option Explicit
Public x As Double
Public y As Double
Private Sub CommandButton1_Click()
TextBox1.Value = x + y
End Sub
And you can test it like that
Sub Demo()
Dim f1 As New UserForm1
Dim f2 As New UserForm2
f1.Show
f2.x = f1.a
f2.y = f1.b
f2.Show
End Sub
PS No checks or whatsover if the values entered in the textboxes are really valid.

a rough way
1) declare some Public variables of Double type
hence put this a the top of any module of your choice in the project
Public aValue As Double, bValue As Double, cValue As Double, dValue As Double
2) change your code as follows
UserForm1
Option Explicit
Private Sub OK_Click()
Dim l As Double, w As Double
aValue = CDbl(a.Value)
bValue = CDbl(b.Value)
cValue = CDbl(c.Value)
dValue = CDbl(d.Value)
l = aValue + bValue
w = cValue + dValue
Me.Hide
With UserForm2
.Show
End With
End Sub
UserForm2
Private Sub OK_Click()
Dim x As Double, y As Double
x = aValue + dValue
y = bValue + cValue
End Sub
and the likes ...

Related

How to reference an element in a class collection

I have a collection of Toggle Buttons along with the object name of the toggle button, as is shown in the watch screen shot
I have tried to reference the sName for the second item in the collection with this line itbcollection.Item(2).sName but it gives me Object does not support this property or method error. What is the appropriate code to get to the sName?
There is a class that is used to capture Mouse Down events, assigned to several ToggleButtons.
Option Explicit
Private WithEvents cTB As MSForms.ToggleButton
Private sName As String
Public Property Let aTB(iTB As ToggleButton)
Set cTB = iTB
sName = cTB.Name
End Property
Private Sub cTB_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal Y As Single)
If Button = 1 Then Exit Sub
Module1.RightClickDay cTB.Caption
End Sub
and at form load a loop finds all ToggleButtons and loads the collection
Dim tbEvent As cObjectArray
Dim TB(42) As Object
x = 0
For Each o In UFShows.Controls
If o.Tag = "T" Then
Set TB(x) = o
x = x + 1
o.Visible = False
Set tbEvent = New cObjectArray
tbEvent.aTB = o
iTBcollection.Add tbEvent
End If
Next o
Please, use the next adapted class code and name it "TGButClass":
Option Explicit
Public WithEvents cTB As MSForms.ToggleButton 'if not Public, it will not be exposed!
Public sName As String 'if not Public, it will not be exposed!
Public Property Set aTB(iTB As MSForms.ToggleButton)'you need Set here (nothing to be extracted, you need to Set)
'and As TooggleButtin means a sheet control
Set cTB = iTB
sName = cTB.Name
End Property
Private Sub cTB_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal Y As Single)
If Button = 1 Then Exit Sub
Module1.RightClickDay cTB.Caption
End Sub
A testing called sub RightClickDay should look like:
Sub RightClickDay(strName As String)
MsgBox strName
End Sub
Then, in the form code module you should paste the next declarations and UserForm_Initializeevent:
Option Explicit
Private objMyEventClass As New Collection
Public tbEvents As TGButClass
Private Sub UserForm_Initialize()
Dim o As Control, x As Long
For Each o In Me.Controls
If o.Tag = "T" Then
x = x + 1
Set tbEvents = New TGButClass
Set tbEvents.aTB = o
objMyEventClass.Add tbEvents
Debug.Print objMyEventClass(x).sName 'to see the (now) exposed sName
End If
Next o
End Sub
Please, send some feedback after testing it.
If something not clear enough, do not hesitate to ask for clarifications.
A similar result can be obtained using an array instead of a collection, too.

How to limit when a Class control triggers?

In this post, I was shown how to create a class for a textbox, so I could use the Textbox_Change event to run other code, like this example from #Storax:
Option Explicit
Public WithEvents tb As MSForms.TextBox
' just to keep track of the box in the grid
Public x As Long
Public y As Long
' Just a simple example for the change event.
' you could use x and y to tell the different textboxes apart
Private Sub tb_Change()
Debug.Print tb.Text, x, y
End Sub
Unfortunately, it works too well.
It fires on every keystroke in the textbox. I think I can work around that, but I'd really like it to wait until the user has finished typing, or tabbed to another control. But Textbox controls in a class module do not have Enter or Exit events.
In my main module, I have lines that change the value of the text box, but I don't always want it to trigger the event. I've tried:
Application.EnableEvents = False
Textbox1.value = "Default"
Application.EnableEvents = True
...but the Change event triggers anyway.
It is indeed possible. Based on this post you need to copy the following code into a textfile, name it catchevent.cls and import it as a class module. This is important as it contains attributes which you cannot enter in the VBE of Excel.
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CatchEvents"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
#If VBA7 And Win64 Then
Private Declare PtrSafe Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, _
ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, _
Optional ByVal ppcpOut As LongPtr) As Long
#Else
Private Declare Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, _
ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As Long) As Long
#End If
Private EventGuide As GUID
Private Ck As Long
Private ctl As Object
'All Other Control-Events also possible
Public Sub OnEnter()
Attribute OnEnter.VB_UserMemId = -2147384830
Select Case TypeName(ctl)
Case "TextBox": MsgBox "Your code for " & ctl.Name & " here!"
Case Else: MsgBox "You entered no TextBox but another control (" & ctl.Name & ")!"
End Select
End Sub
Public Sub OnExit(ByVal Cancel As MSForms.ReturnBoolean )
Attribute OnExit.VB_UserMemId = -2147384829
Select Case TypeName(ctl)
Case "TextBox": MsgBox "Your code for " & ctl.Name & " here!"
Case Else: MsgBox "You left no TextBox but another control (" & ctl.Name & ")!"
End Select
End Sub
Public Sub ConnectAllEvents(ByVal Connect As Boolean)
With EventGuide
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
ConnectToConnectionPoint Me, EventGuide, Connect, ctl, Ck, 0&
End Sub
Public Property Let Item(Ctrl As Object)
Set ctl = Ctrl
Call ConnectAllEvents(True)
End Property
Public Sub Clear()
If (Ck <> 0) Then Call ConnectAllEvents(False)
Set ctl = Nothing
End Sub
Then you need to adjust your code in the userform like that
Option Explicit
Private AllControls(0 To 49) As New CatchEvents
Dim Grid(1 To 10, 1 To 5) As MSForms.TextBox
Private Sub UserForm_Initialize()
Dim x As Long
Dim y As Long
Dim i As Long
For x = 1 To 10
For y = 1 To 5
Set Grid(x, y) = Me.Controls.Add("Forms.Textbox.1")
AllControls(i).Item = Grid(x, y)
i = i + 1
With Grid(x, y)
.Name = "TextBox_" & x & "_" & y
.Width = 50
.Height = 20
.Left = y * .Width
.Top = x * .Height
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
End With
Next y
Next x
End Sub
Further reading here
PS: Why you need to import code sometimes: Code Attributes
https://stackoverflow.com/a/34688164/6600940
Use a class level variable to track where or not you want to listen for grid events.
Private Grid(1 To 10, 1 To 5) As New TextBoxListener
Private GridEventsEnabled As Boolean
Public Sub TextBoxGridChange(TextBox As MSForms.TextBox)
If Not GridEventsEnabled Then Exit Sub
Debug.Print TextBox.Value
End Sub
Private Sub UserForm_Initialize()
Dim x As Long
Dim y As Long
For x = 1 To 10
For y = 1 To 5
With Grid(x, y)
Set .TextBox = Me.Controls.Add("Forms.Textbox.1")
Set .UserForm = Me
With .TextBox
.Name = "TextBox_" & x & "_" & y
.Width = 50
.Height = 20
.Left = y * .Width
.Top = x * .Height
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
End With
End With
Next y
Next x
GridEventsEnabled = True
End Sub

One event sub for multiple textboxes instead of declaring it multiple time?

Is it possible to have one change event procedure for multiple textboxes in the same workbook?
If for example, I have a textbox named "textbox3" in the 1st, 2nd, and 3rd sheet of the workbook and I would like this single code below to work for all of them (the textboxes) rather than having to declare it on each sheet. Right now, I have to declare the same procedure on all the sheets but I only want to declare it once since it does the same thing on all of them.
'my procedure
Sub testObj()
Dim i As Integer, obj As oleobject
Set ac = ThisWorkbook.ActiveSheet
For Each obj In ac.oleobjects
If TypeName(obj.Object) = "TextBox" And obj.name = "TextBox3" Then
i = i + 1
ReDim Preserve TextArray(1 To i)
Set TextArray(i).TextBoxEvents = obj
End If
Next obj
Set obj = Nothing
End Sub
'My class1
Public WithEvents TextBoxEvents As MSForms.TextBox
'Public WithEvents TextBoxEvents As OLEObject
Private Sub TextBoxEvents_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = vbKeyTab Then
TextBox12.Activate
End If
End Sub
Here's an example
Class module named clsTextBox:
Option Explicit
Public WithEvents TextBoxEvents As MSForms.TextBox
Private Sub TextBoxEvents_Change()
Debug.Print TextBoxEvents.Name & ": " & TextBoxEvents.Text
End Sub
'Private Sub TextBoxEvents_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
' If KeyCode = vbKeyTab Then
' TextBox12.Activate 'unclear what you're aiming for here?
' End If
'End Sub
Regular module:
Dim ColTB As Collection
Sub testObj()
Dim i As Integer, obj As OLEObject, ac As Worksheet
Set ac = ThisWorkbook.ActiveSheet
Set ColTB = New Collection
For Each obj In ac.OLEObjects
If TypeName(obj.Object) = "TextBox" Then
ColTB.Add EventObj(obj.Object)
End If
Next obj
Set obj = Nothing
End Sub
Function EventObj(obj As MSForms.TextBox) As clsTextBox
Dim o As New clsTextBox
Set o.TextBoxEvents = obj
Set EventObj = o
End Function

Passing variables from one procedure to another

How can I use a vairable from Sub Main, in another sub.
Example
Sub Main()
Dim x As Long
End Sub
Sub Test() Dim y = x End Sub
Is this possible on vb.net
You could pass it to a function and return it again:
Sub Main()
Dim x As Long
' Give x a value
x = Test(x)
' Do something else with x
End Sub
Function Test(x As Long) As Long
' Do something with x
Return x
End Sub
Or declare it as a global variable:
Dim x As Long
Sub Main()
' Do something with x
End Sub
Sub Test()
Dim y = x ' Do something else
End Sub
There is a very easy way to do this.
Declare the value out of all the Subs on the top.
Public Class Form 1
Dim x As Long
Private Sub Main ()
End Sub
Private Sub Test ()
End Sub
End Class
And then write y = x on whichever Sub you want!

Using a sub var into other sub visual basic

How can i get a var value from a sub by calling it in other sub ? like
Sub test()
Dim a As Integer
a = 1
End Sub
Sub testshow()
MessageBox.Show(test.a)
End Sub
In VBA (which your tag states) you need to change Sub into Function which will be:
Function test()
Dim a As Integer
a = 1
test = a
End Function
Sub testshow()
MsgBox test
End Sub
EDIT after comment: If you using more then one variable then:
Function test(whichVar)
Dim a As Integer
If whichVar = 1 then
a = 100
ElseIf whichVar = 2 Then
a = 200
'etc...
End if
test = a
End Function
Sub testshow()
MsgBox test(2) 'will give you 200
End Sub