Mapping a variable from an Activex control - [VBA] - excel

Hi I am trying to use a publicly declared variable that has a value assigned to it upon clicking one of two optionbuttons inside a userform.
The userform does show and I can click either of the two buttons i have in the frame I created, but the variable does not pick up the click or actually I don't know what I'm doing with the userform controls. And i don't think the form hides either, clicking the optionbutton seems to be a dummy click, it highlights but does nothing after that.
Plus i'm not sure if the code itself needs to be placed on the userform' code section or the module code section, does it make a difference? Currently running it on the module code section.
Option Explicit
Dim C As String
Public Sub OptionButton1_Click()
C = "Delivery"
End Sub
Public Sub OptionButton2_Click()
C = "Holiday"
End Sub
Public Sub Optionbutton()
Userform1.Show
Sheet1.Cells(1, 1).Value = C
Userform1.Hide
End Sub
********--Edit-*********
I guess Zack's method gets the job done, printing to Cell A1, but what I was really after is although the event handler needs to be private, running debug points out that the execution stops when the private sub ends, which could be optionbutton1 or optionbutton2's click event, which is what led me to believe the click was a dummy click.
But what I was really after is for the C variable to be available in the public sub that I execute from, is there a way for the execution to divert to the public sub after the C is assigned a value in the optionbutton private sub?
For example if the C was assigned in the private sub but my success criteria were defined by the successful execution of the public Sub defined above, because i have a larger macro where that variable will be used inside the public sub, and this is only part of the problem, or the problem I can't solve.

If I understand correctly, your form has two option buttons. Thus, your form's code-behind should look like this:
Option Explicit
Private Sub OptionButton1_Click()
C = "Delivery"
End Sub
Private Sub OptionButton2_Click()
C = "Holiday"
End Sub
This assumes there's a global variable C declared somewhere, in a standard module:
Option Explicit
Public C As String
Public Sub Optionbutton()
Userform1.Show
Sheet1.Cells(1, 1).Value = C
End Sub
Note that this displays the form modally, which means the next instruction only runs after the form is closed/destroyed. For that reason, there's no need for the subsequent Userform1.Hide call: the form is already gone by then.
Once you're comfortable with manipulating global variables from a form's code-behind, read UserForm1.Show (I wrote that article) for insight on a vastly more robust way to go about this.

Try this:
You will need to tell the control where to place the value after the button is clicked.
Option Explicit
Dim C As String
Private Sub OptionButton1_Click()
C = "Delivery"
Sheet1.Cells(1, 1) = C
Userform1.Hide
End Sub
Private Sub OptionButton2_Click()
C = "Holiday"
Sheet1.Cells(1, 1) = C
Userform1.Hide
End Sub
Public Sub Optionbutton()
Userform1.Show vbModeless

Related

Userform initialize without rewriting data in the cell

I have been wondering about this one for a while now.
Let's say I have a formula in A1, Worksheet("Main")
=IF(B2="English";"Good morning";"Guten Morgan")
Then I have userform with code:
Private Sub TextBox1_Change()
ThisWorkbook.Worksheets("Main").Range("A1").Value = Me.TextBox1.Text
End Sub
Private Sub UserForm_Initialize()
Me.TextBox1.Text = ThisWorkbook.Worksheets("Main").Range("A1").Value
End Sub
How can I make it work so, that if I don't input anything into textbox, it will keep displaying functions result. If I will start to type text into textbox it will input my typed text to A1. Now if I open the userform it will overwrite A1 with the text in textbox and there will be no formula anymore. So if I change language in B2 result will no longer be interfaced into textbox.
Can be also some other approach with VBA. Everything is acceptable as long as logic will work.
I have tried to use textbox properly, something like linkedsource or similar, but it is crashing excel workbook sometimes. That's why I am trying to avoid it.
EDIT:
Thank you for suggestions! I have tried to implement this somehow but still don't get it. I am creating variable where I want to store result from ThisWorkbook.Worksheets("Other Data").Range("L49").Value then I would like to use it in Userform Me.TextBox14.Text to be displayed. Then once it is changed in Me.TextBox14.Text and Enter button has been pressed it should change also in ThisWorkbook.Worksheets("Other Data").Range("L49").Value.
Here is my current code I am trying to play with:
Private ProjectClass As String
Private Sub TextBox14_Enter()
ThisWorkbook.Worksheets("Other Data").Range("L49").Value = ProjectClass
End Sub
Private Sub UserForm_Initialize()
Me.TextBox14.Text = ProjectClass
End Sub
The TextBox.Enter event isn't fired when the user presses Enter, but when the control is entered - that is, when it gets the focus and a caret/cursor starts blinking inside it. You'll want to update the backing variable when the value is modified:
Private Sub TextBox14_Enter()
'runs when the control gets focus
End Sub
Private Sub TextBox14_Exit()
'runs when the control loses focus
End Sub
Private Sub TextBox14_Change()
'runs whenever the value changes (real-time)
End Sub
So in this case I'd go with the TextBox.Change event handler, and make it update the variable (not the worksheet):
Private ProjectClass As String
Private Sub TextBox14_Change()
ProjectClass = TextBox14.Text
End Sub
Now the problem is that the ProjectClass value needs to be accessible from outside the form, so that the caller can set an initial value. One way to do this could be to expose it as a property - one property (get+let) for each field you want to seed a value for:
Public Property Get ProjClass() As String
ProjClass = ProjectClass
End Property
Public Property Let ProjClass(ByVal value As String)
ProjectClass = value
ApplyModelProperties
End Property
Private Sub ApplyModelProperties()
TextBox14.Text = ProjectClass
'...
End Sub
Now from outside the form, at the call site (the code that's showing this dialog), you can seed the value from the worksheet, and the form never needs to know or care that a worksheet was involved:
With New UserForm1
.ProjClass = ThisWorkbook.Worksheets("Other Data").Range("L49").Value
.Show
MsgBox .ProjClass
End With
Note that because the value is exposed as a property, the calling code doesn't need to know about TextBox14 anymore.

Call same SUB from multiple buttons in VBA

I have multiple buttons (active x) on a spreadhseet in same column but different rows. These buttons capture the start time of an activity.
If button 1 is pressed cell next to it should be populated by current time.
If button 2 is pressed cell next to it should be populated by current time. and so on.....
I have written a SUB in VBA as follows:
Private Sub StartTimer_Click()
Range("I4").Value = Now
End Sub
I do not want to repeat this code for each button action. Please let me know how it can be made dynamic.
A simple WithEvents example:
in a class (named clsButtons):
Private WithEvents Bt As MSForms.CommandButton
Property Set obj(b As MSForms.CommandButton)
Set Bt = b
End Property
Private Sub Bt_Click()
'uses the right of the name of the CommandButton
Cells(1 + Right(Bt.Name, 1) * 3, 9).Value = Now
End Sub
In the sheetcode (the one with the buttons):
Dim myButtons As Collection
Private Sub Worksheet_Activate()
Dim ctl As OLEObject
Dim ButtonClass As clsButtons
Set myButtons = New Collection
For Each ctl In Sheet1.OLEObjects
If ctl.progID = "Forms.CommandButton.1" Then
Set ButtonClass = New clsButtons
Set ButtonClass.obj = ctl.Object
myButtons.Add ButtonClass
End If
Next ctl
End Sub
Create a standard module and put the procedure in there.
While it is possible to share a procedure in a private module, it's best practice to put any shared procedures in a shared module.
In the VBA Editor click Insert > Module,
Paste into there, and give it a unique name. Using your example you could do something like:
Public Sub SetTimeValue()
Range("I4").Value = Now
End Sub
...then call this public stub from your other one, like:
Private Sub StartTimer_Click()
SetTimeValue
End Sub
...and from any other locations where you need to call your code.
I assume that you have more than one line of code for the actual procedure you're concerned about, otherwise copying it repeatedly isn't really a concern.
More Information:
MSDN : Understanding Scope and Visibility
Office Support : Scope of variables in Visual Basic for Applications
Chip Pearson : Understanding Scope Of Variables And Procedures
PowerSpreadsheets : Excel VBA Sub Procedures: The Complete Tutorial
MVP : Cut out repetition using subs and functions with arguments

VBA Userforms Show the Same Userform again and again

currently i am programming a excel macro. The macro shows a Userform.
In the Userform the User can Select something. After the User has selected something i call Userform.Hide to Hide the Userform and to read the Selection from the Form. After the selection was read i call Unload Userform. Now the Code interacts with the selection. I want to do this in a loop but when the Code trys to show the Userform the second time. I get a exception that the Form is already displayed. I cant understand it, because i called Unload Userform. When i do it in debug mode everthing works as it should.
Userform Code
Private Sub Image1_Click()
SelectCard 1
End Sub
Private Sub Image2_Click()
SelectCard 2
End Sub
Private Sub SelectCard(number As Integer)
SelectedNumber = number
Me.Hide
End Sub
Public Sub CardSelector_Activate(Cards As Cards)
Dim c As card
For Each Key In Cards.CardDictionary.Keys
Set c = Cards.CardDictionary.Items(Key - 1)
If c.value = 1 And c.played Then
Image1.Enabled = False
End If
If c.value = 2 And c.played Then
Image2.Enabled = False
End If
Next Key
number = SelectedNumber
CardSelector.Show
End Sub
Code in the ClassModule i call this in a loop
Sub Costum(Spalte As Integer, Zeile As Integer, SpalteBeginn As Integer, Cards As Cards, CardsOpponent As Cards)
CardSelector.CardSelector_Activate Cards
Dim c As card
Dim number As Integer
number = CardSelector.SelectedNumber
Set c = Cards.CardDictionary.Items(CardSelector.SelectedNumber - 1)
SetCardAsPlaced c, Zeile, Spalte, SpalteBeginn
Unload CardSelector
End Sub
Can someone help me here ?
I am not sure if I fully understand your issue, but this is how I invoke a form using VBA. This is assuming you have a Cancel and OK button:
In the form:
Option Explicit
Private m_ResultCode As VbMsgBoxResult
Private Sub btnCancel_Click()
Call CloseWithResult(vbCancel)
End Sub
Private Sub btnOK_Click()
' Store form control values to member variables here. Then ...
Call CloseWithResult(vbOK)
End Sub
Private Sub CloseWithResult(Value As VbMsgBoxResult)
m_ResultCode = Value
Me.Hide
End Sub
Public Function ShowMe(Optional bNewLayerOptions As Boolean = True) As VbMsgBoxResult
' Set Default to Cancel
m_ResultCode = vbCancel
' Execution will pause here until the form is Closed or Unloaded
Call Me.Show(vbModal)
' Return Result
ShowMe = m_ResultCode
End Function
Then, to call it (please note that frmLayers is my own VBA form object - you would use yours):
Dim dlgLayers As New frmLayers
If (dlgLayers.ShowMe(False) = vbOK) Then
' Proceeed
End If
Does this help you with your issue? I am sorry if I have misunderstood, and I will remove my answer if needed.
Things like xxxxx_Activate etc. are event handlers called by the framework. So, for example, there is an event for activate and an event for initialize. You don't normally have to directly call these yourself if you set your code up correctly. See https://support.microsoft.com/en-us/kb/138819.

"Include" in Excel VBA?

I have a user-form which is made up of many subs, this is assigned as a macro to a button on the worksheet. When the user is finished with this user-form they can press a button on it which causes its visibility to become false and when entered again everything appears how it was left resulting in a save like feature.
I now need to apply this to multiple buttons on the worksheet and each user form needs to have the exact same code and same buttons but be a separate form as each individual button requires it's own save like feature. The way I was planning on doing this was to copy the existing user form and paste it many times with different names however, if a modification is required it will take a long time to carry out therefore, is there a method such as "include" which could use a base module from which all the code is accessed so that if I ever need to change anything I just do it on that one module and everything else updates via the include?
EDIT:
I now have a public function called costing() and am getting an error when I used:
Private Sub material_Change()
Call costing
End Sub
You can have multiple instances of the same form. You can use this to retain multiple sets of form values
Try this:
Create your form, as usual. Let's call it MyForm
Create several buttons on your sheet. My example uses ActiveX buttons, but Form Control buttons can be used too. Let's call them CommandButton1 and CommandButton2
In your form module, include a Terminate Sub, which includes this code
Private Sub UserForm_Terminate()
' any other code you may need...
Unload Me
End Sub
The Form buton to save/Hide the form needs to be
Private Sub btnSaveAndHide_Click()
Me.Hide
End Sub
The Sheet Button code is as follows
The code is identical for each button (and calls a common Sub), and each button has its own Static form variable.)
The Error handler is needed to deal with the case a form is not properly closed. In this case the instance no longer exists, but the local Static variable is also not Nothing
Example shows form shown as Modeless, you can change this to Modal if you want.
Private Sub CommandButton1_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub CommandButton2_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub ShowMyForm(frm As MyForm)
If frm Is Nothing Then Set frm = New MyForm
On Error GoTo EH
frm.Show vbModeless
Exit Sub
EH:
If Err.Number = -2147418105 Then
On Error GoTo 0
Set frm = Nothing
Set frm = New MyForm
frm.Show
End If
On Error GoTo 0
End Sub
End result: multiple copies of the same form, each with their own values
In responce to comment How would I access the variables inside of each user form externally
In the example above the Form instances are only accessable in the Command Button Click Handler routines, or within the Form module itself. If you can write your code in the form module, then no change is needed.
To make the Form instances available elsewhere, consider moving their declaration to Module Scope of a standard Module. You could declare them as, eg individual variables, an array (either static or dynamic), a Collection, a Dictionary. Which structure is best will depend on how you want to manage and access your form instances.
For example, a Static Array: Code in a standard Module
Option Explicit
Global MyForms(1 To 2) As MyForm
Update the CommandButton code to
Private Sub CommandButton1_Click()
ShowMyForm Module1.MyForms(1)
End Sub
Private Sub CommandButton2_Click()
ShowMyForm Module1.MyForms(2)
End Sub
Private Sub ShowMyForm(frm As MyForm) no change, same as before
The code works the same as before, but you can now access the Global variable in a standard Module
Sub Demo()
Dim i As Long
For i = LBound(MyForms) To UBound(MyForms)
If Not MyForms(i) Is Nothing Then
MsgBox "Form " & i & " Value = " & MyForms(i).TextBox1.Value
End If
Next
End Sub
You don't need an "Include" (none exists in VBA); all you need to do is create a module and make the common methods public.
For example, if you create a module and have a function like this:
Public Function Add(first As Integer, second As Integer) As Integer
Add = first + second
End Function
Then you can access it like this from another module/form/class module:
Sub test()
MsgBox Add(3, 6)
End Sub

User form can't pass value to module

I've inserted a user form into a project that already contains a bunch of modules. By playing around with the code in the user form, I've verified that I can return the value from a combo box.
User form code:
Public SelectedPacking As Integer
Private Sub CancelButton_Click()
UserForm1.Hide
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk
'Return list index value to cell C50
Worksheets("Inputs & Results").Range("C50") = SelectedPacking
Unload UserForm1
End Sub
My problem is that I can't pass this value on to any of the macros written in the modules.
Module code:
Public Sub ShowComboBox()
UserForm1.Show
End Sub
Public Sub DemoDialogOk()
ival = SelectedPacking
'Return value of ival (list index value from combo box) to cell C17
Worksheets("Packed bed (Random)").Range("C17") = ival
End Sub
Obviously the module contains more useful code, but I've commented out everything to try and figure out where I'm going wrong. I've been changing some things around, but I still can't get anything to appear in cell C17, so I think I'm missing something fundamental.
I think two options: 1) change DemoDialogueOK to accept variables:
Public Sub DemoDialogOk(SelPack as integer)
' ival = SelectedPacking
Worksheets("Packed bed (Random)").Range("C17") = SelPack
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk(SelectedPacking)
...
End Sub
Or option two: fully qualify the variable from the useform i.e:
Public Sub DemoDialogOk()
ival = ufYourForm.SelectedPacking
...
End Sub
Public variables in userforms don't appear to be as "public" as module level...
Tipping on top of Simon's answer, you could pass the entire userform if you'd like. This would give you access to all the pieces of it and is especially useful if you need to do some validation on, say, different checkboxes being checked or not.
Sub inOurUserForm()
Call inADifferentModule(Me) 'Passes this userform
End Sub
Sub inADifferentModule(ourForm As UserForm1)
'Passed the form, and using it like a class (As whatever the form is called)
If ourForm.chkYes = True Then
'Do something
Else
'Do something else, like
ourForm.chkYes = False 'Because we are passing the object itself _
rather than a copy, at least in my understanding
End If
End Sub
And you don't necessarily need to pass the userform, as you could just reference it as an object itselft e.g.
UserForm1.chkYes
A very easy solution to this would be to declare a variable within the Userform (UserForm1 in this example)
Public Pass as string
This Pass would contain the string where you store the password. Once you store the password, you can hide the form
Me.Hide
Within the module, you can open the Form as modal
UserForm1.Show vbModal
Now after all the code inside the userform is run, the password can be retrieved within the module -
UserForm1.Pass
You can then unload the hidden form
unload UserForm1

Resources