I have a VBA code that takes user inputs (criteria) via a userform, creates a list of items that meet said criteria, and randomly outputs one of the list items via message box.
I want to create a second userform that will open after the above code completes, and have the userform display the random output.
I have a few questions regarding how this can be done.
I want to the second userform to contain a label, whose caption will reflect the random output.
I want the second userform to allow the user to re-execute the original code (ie. to change the random result)
Anyone have experience with this?
I'd use a Property procedure in the second form to get the random value.
As an example in a new blank workbook:
Insert a new module and paste in the code below. This code represents your code which outputs a random result.
Public Function RandomNumber() As Double
RandomNumber = Rnd(10)
End Function
Create a new userform (UserForm2), add a command button (CommandButton1) and a label (Label1). Add the below code to this form. I've put four procedures in there. MyProp sets the value of ThePassedNumber, UpdateLabel1 updates the value shown in the label, UserForm_Initialize executes when the form loads, CommandButton1_Click executes when you click the button.
Private ThePassedNumber As Double
Property Let MyProp(TheNumber As Double)
ThePassedNumber = TheNumber
UpdateLabel1
End Property
Private Sub CommandButton1_Click()
ThePassedNumber = RandomNumber
UpdateLabel1
End Sub
Private Sub UserForm_Initialize()
UpdateLabel1
End Sub
Private Sub UpdateLabel1()
Me.Label1.Caption = ThePassedNumber
End Sub
Add a second form (UserForm1) and add a command button (CommandButton1) and place this code within the form.
It will create a new instance of the userform, pass a random value to it and then display the form.
Private Sub CommandButton1_Click()
Dim frm2 As UserForm2
Set frm2 = New UserForm2
frm2.MyProp = RandomNumber
frm2.Show
End Sub
Now you just need to rewrite the RandomNumber function to return your list item rather than a number.
I imagine you just want to use an endless loop that the user breaks when canceled.
Something like this:
Sub MySub()
Dim myRandOutput As Single
Do
myRandOutput = myRand()
Loop While MsgBox(myRandOutput, vbRetryCancel) = vbRetry
End Sub
Function myRand() As Single
myRand = Rnd()
End Function
Related
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.
I developed many UDFs and macros in VBA for Excel 2016. One of my macros uses an Inputbox to get data used subsequently by the macro. I want to replace the Inputbox with a user form. I have created the user form, with one text box. I want to activate the user form, populating the text box with the default data, and return the text box data to the macro when OK is selected. I have searched extensively for an end-to-end example for all the the code needed to do this, with no luck. Does an example for this simple problem exist?
Add a Property to your user form. For this answer, let us use the following code within the user form.
Public Property Get MyResult() As String
' You may want to do any manipulation here
' including converting to a number, in which case the return type should be changed (*)
MyResult = TextBox1.Text
End Property
(*) If you are doing conversion, you can have another function in your user form to disable the "OK" button until they have valid convertible data in the text box.
You also want to know if they have hit "Cancel"
Public Property Get Cancelled() As Boolean
Cancelled = pCancelled ' Declare pCancelled as a Boolean in the scope of the form
End Property
Public Sub CancelButton_Click() ' Standard click event for the button
pCancelled = True
Me.Hide
End Sub
Public Sub OKButton_Click() ' Standard click event for the button
pCancelled = False
Me.Hide
End Sub
In your calling macro
MyForm.Show ' This is modal, so will block execution until a response is provided
If Not MyForm.Cancelled Then
Debug.Print MyForm.MyResult
'Do something with MyForm.MyResult
End If
UnLoad MyForm ' assuming you do not want to re-use this form as part of your logic.
There is an example of how you can pass the value to a form and get the result back. The approach uses Scripting.Dictionary object created within standard module scope and passed to userform to allow values to be changed. So it makes possible to send the default values to userform, and keep the result values in the dictionary even after the userform is closed and unloaded. You may have multiple values, just add the necessary quantity of keys to the dictionary, e. g. oData("property1"), oData("property2"), etc.
Add a standard module to the project and put the below code into it:
Option Explicit
Sub Test()
Dim oData
' Set default value and show form
Set oData = CreateObject("Scripting.Dictionary")
oData("") = "Some default text"
UserForm1.ShowForm oData
' Wait until user close form
Do While IsUserFormLoaded("UserForm1")
DoEvents
Loop
' Output returned value
MsgBox oData("")
End Sub
Function IsUserFormLoaded(UserFormName As String) As Boolean
Dim oUF As Object
For Each oUF In UserForms
If LCase(oUF.Name) = LCase(UserFormName) Then
IsUserFormLoaded = True
Exit Function
End If
Next
End Function
Add a userform module named UserForm1 to the project, place controls as shown:
And put the below code into the userform module :
Private opData
Public Sub ShowForm(oData)
Set opData = oData
Me.TextBox1.Value = opData("")
Me.Show
End Sub
Private Sub UserForm_Initialize()
If TypeName(opData) <> "Dictionary" Then Set opData = CreateObject("Scripting.Dictionary")
End Sub
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub CommandButton2_Click()
opData("") = Me.TextBox1.Value
Unload Me
End Sub
I have a worksheet that runs a weightlifting meet. Last year I had created a tab that would give information on the current lifter and on who was lifting next.
left side - Display, right side - input tab
When I input an "x" in columns R, S, T, or W on the data tab, it changes the information in the BenchGenerator tab, like so:
Updated Display tab
What I want to do is make a userform display to run on a different screen so people can see this information. I had accomplished this last year by widening excel and using two view windows - display on the second screen and running the meet on the computer. It was ok but very clunky looking. With a floating userform tab, it would look fantastic. I am new to this, but got the form floating:
Private Sub Worksheet_Change(ByVal Target As Range)
UserForm1.Show (vbModeless)
End Sub
And got the labels to initially populate:
Userform Display
Using this code:
Private Sub UserForm_Activate()
UserForm1.Label1.Caption = Sheets("BenchGenerator").Range("c4").Value
UserForm1.Label2.Caption = Sheets("BenchGenerator").Range("c5").Value
UserForm1.Label3.Caption = Sheets("BenchGenerator").Range("c6").Value
UserForm1.Label4.Caption = Sheets("BenchGenerator").Range("d3").Value
UserForm1.Label5.Caption = Sheets("BenchGenerator").Range("d4").Value
UserForm1.Label6.Caption = Sheets("BenchGenerator").Range("d5").Value
UserForm1.Label7.Caption = Sheets("BenchGenerator").Range("d6").Value
End Sub
What it doesn't currently do is update the captions when I input the "x" in the data tab.
As I mentioned, this is my first foray into userforms and looking through mountains of code trying to figure this out, it will not be my last as there is lots to accomplish with them.
Thanks in advance for any help!
You're pretty close to getting this to work. The problem is that your calling a new form every time a change occurs.
Declare your form as an object outside of the Sub that creates (Show) it.
You can then access it to update labels from another Sub that has the same scope.
Create an UpdateForm sub for example and call it from your Worksheet_Change event.
Try this, place the following code in a new Module:
Dim myForm As Object
Sub launchForm()
Set myForm = UserForm1
myForm.Show (vbModeless)
End Sub
Sub updateForm()
Dim wks As Worksheet
Set wks = Sheets("BenchGenerator")
'Update label values here
myForm.Label1.Caption = wks.Range("C4").Value
myForm.Label2.Caption = wks.Range("C5").Value
myForm.Label3.Caption = wks.Range("C6").Value
myForm.Label4.Caption = wks.Range("D3").Value
myForm.Label5.Caption = wks.Range("D4").Value
myForm.Label6.Caption = wks.Range("D5").Value
myForm.Label7.Caption = wks.Range("D6").Value
End Sub
If you use Worksheet_Change to update the form you'll want to verify the form exist or just skip any errors in the event if it doesn't.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
updateForm
End Sub
Not sure if there is an easier way to do it, but I made a concatenation routine to batch out my label setup and updates to quickly create/copy/paste all the code.
Just in case anyone didn't know how to do this
I have knowledge of the RANDBETWEEN() function inside the cells of Excel. This only returns one (1) value. I would like to assign this to a button to return a different value every time the button is hit.
I can create the button, I have the cell. How can I link the two?
Long story short, every time a button is clicked a cell (lets say cell a1) gets a different value between values a to b.
Try below
Private Sub CreateRandom()
'random number between 11 and 100
Debug.Print NewRandom(11, 100)
End Sub
Private Function NewRandom(ByVal FromLimit As Integer, ByVal ToLimit As Integer) As Integer
Randomize
randomNumber = Int((ToLimit - FromLimit) * Rnd) + FromLimit
NewRandom = randomNumber
End Function
Private Sub CommandButton1_Click()
'random number between 30 and 100
Sheets("Sheet2").Range("A1").Value = NewRandom(30, 100)
End Sub
MI Consol:
Sheets:
Private Sub CommandButton1_Click()
Call p
End Sub
Private Sub CommandButton3_Click()
Call save
End Sub
Private Sub CommandButton4_Click()
Call EMail
End Sub
Private Sub CommandButton5_Click()
Call iSMS
End Sub
Private Sub CommandButton6_Click()
Call continue
End Sub
Private Sub CommandButton7_Click()
Call Continue1
End Sub
If you're still using the RANDBETWEEN function and you just want it to generate a new value on command, just press F9 to force Excel to recalculate all formulas. RANDBETWEEN will then generate a new random number. (If it doesn't change value, it's just because it generated the same random number by chance. Keep pressing F9 to get new random numbers.)
Note that changing any formula on the spreadsheet will also cause the RANDBETWEEN function to generate a new value.
If you want a new random number to be generated ONLY when you command it to, then you do need to create a macro, which can be easily called through a button. I'm assuming you're not familiar with macros at all, so the following is a simple solution.
The following website explains how to create a simple button that will run a macro. (Note that this specific method only works with the ActiveX button, not the Forms button.)
https://www.tutorialspoint.com/vba/vba_excel_macros.htm
You will just need to replace the code in their example (MsgBox "Hi") with the following code:
Range("A1") = WorksheetFunction.RandBetween(a,b)
Replace a and b with the numbers you want to get a random number between. You can do this a few ways:
If you always want it between the same two numbers (e.g. 1 and 10), you can hard-code them into the macro:
Range("A1") = WorksheetFunction.RandBetween(1,10)
If you want it between two numbers recorded on the same spreadsheet (e.g. in cells B1 and B2), you can refer to the cells:
Range("A1") = WorksheetFunction.RandBetween(Range("B1"),Range("B2"))
Or you can ask for the numbers in popups:
a = InputBox("Enter a")
b = InputBox("Enter b")
Range("A1") = WorksheetFunction.RandBetween(a, b)
Note that the website misses a couple of important steps you must perform before the button will be able to used:
Close the VBA window (not actually necessary, but there's no need to leave it open)
Exit Design Mode. On the Developer tab in Excel, the Design Mode button should still be highlighted. Click it to un-highlight it. You can now click your button.
Note that if you want to change the code for the button again, you need to click Design Mode again to highlight it again, then you can double click the button to view the code again.
I have an Excel spreadsheet that is separated into different sections with named ranges. I want to hide a named range when a checkbox is clicked. I can do this for one checkbox, but I would like to have a single function that can hide the appropriate section based on the calling checkbox. I was planning on calling that function from the event_handlers for when the checkboxes are clicked, and to pass the checkbox as an argument.
Is there a way to access the checkbox object that calls the event handler?
This works:
Sub chkDogsInContest_Click()
ActiveSheet.Names("DogsInContest").RefersToRange.EntireRow.Hidden = Not chkMemberData.Value
End Sub
But this is what I would like to do:
Sub chkDogsInContest_Click()
Module1.Show_Hide_Section (<calling checkbox>)
End Sub
These functions are defined in a different module:
'The format for the the names of the checkbox controls is
'CHECKBOX_NAME_PREFIX + <name>
'where "name" is also the name of the associated Named Range
Public Const CHECKBOX_NAME_PREFIX As String = "chk"
Public Function CheckName_To_SectionName(ByRef strCheckName As String)
CheckName_To_SectionName = Mid(strCheckName, CHECKBOX_NAME_PREFIX.Length() + 1)
End Function
Public Sub Show_Hide_Section(ByRef chkBox As CheckBox)
ActiveSheet.Names(CheckName_To_SectionName(chkBox.Name())).RefersTo.EntireRow.Hidden = True
End Sub
Since you're using regular (Active-X) checkboxes on a normal worksheet, then your best bet is to create a Click event for each sub, then call one routine for the Hide with the parameter of the checkbox name, like:
Private Sub chkCheckBox1_Click()
If chkCheckBox1.Value = True Then
Call RangeHide("CheckBox1")
End If
End Sub
Private Sub RangeHide(rangetohide As String)
Range(rangetohide).EntireRow.Hidden = True
End Sub
I think the answer is to create another class that has a checkbox object as a part of it and declare that object "WithEvents" Then I can create a method chkBox_clicked() that will be called whenever any checkbox that is a member of that class is clicked. I can also store the range within the object.
http://www.cpearson.com/excel/Events.aspx
Has more info... Great site btw for excel VBA.
EDIT: This does not work. See my comment below.