Having problems using user forms in module - excel

I initially used Application.Inputbox to take the user input which worked fine. But when I wanted to increase the options it exceeded 254 character limit. Thus had to use userform. I am new to userforms.
I used user input to choose the case statements.
The userform basically contains a combobox(CmbType), an attached label and the commandbutton(ok). It looks fine and added the RowSource property in the property. All the options could be seen when executed.
Userform Complete Code:
Sub ok_Click()
Type = CmbType.Value
Unload UserForm1
End Sub
Module Code:
Sub Ingredients()
Dim statements
UserForm1.Show
Select Case Type
Case Is= “Fruits”
….
Case Is =”Vegetables”
…
End Select
Unload UserForm1
…..
…..
I am getting the userform but it is not closing and basically not going to the case statements.One more help that I need is to create a numeric to be used in case statement like 1-Fruits,2-Vegetables etc.

Looks like you are dealing with variable scope.
Create a module called globals. In the module declare
Public iType as integer
You can access that from anywhere in your project.
Don't use the variable name Type. It is a reserved word.
In the form ok button click use
iType = CmbType.Value
Unload me
In the module use
UserForm1.Show
Select Case iType
Case 1
'Fruits
Case 2
'Vegetables
Case Else
'Not sure what this food is
End Select
Here is a bit more info on how case statements can be used. http://www.techonthenet.com/excel/formulas/case.php
Also, look in to "Option Explicit". Adding that at the top of each form and module will let you know of issues like variables that you are trying to use that are not in scope.

Related

Blank values in user form

Trying to get values from userform in module but it always shows blank.
EmailIdForm:
Private Sub CLRBTN_Click()
Call UserForm_Initialize
End Sub
Private Sub OKBTN_Click()
ToInput = EmailIdForm.ToInput.Value
CCInput = EmailIdForm.CCInput.Value
Unload EmailIdForm
End Sub
Private Sub UserForm_Initialize()
ToInput.Value = ""
CCInput.Value = ""
ToInput.SetFocus
End Sub
Module:
Public ToInput As String
Public CCInput As String
Sub EmailInput()
EmailIdForm.Show
MsgBox ToInput
MsgBox CCInput
End Sub
I think the problem is the visibility of the modul level variables ToInput and CCInput. If you name the modul MyModul just add the scope to the variables variables. See the modified code here:
Private Sub OKBTN_Click()
MyModul.ToInput = EmailIdForm.ToInput.Value
MyModul.CCInput = EmailIdForm.CCInput.Value
Unload EmailIdForm
End Sub
To elaborate on the comments;
10000% read UserForm1.Show. It might take some time to understand it (and a lot of googling things) but if you use UserForms it's worth the read to better understand how they work and how to work with them.
If you have a control on your userform with the same name as your public variable, the control will take precedence over the public variable.
So it is assumed that your TextBox names are ToInput and CCInput respecitvely.
When writing code in the UserForm code module, you could access these controls in 3 different ways (let's use the ToInput textbox in our example):
EmailIdForm.ToInput 'Explicitly declare which UserForm we are accessing the control on.
Me.ToInput 'The 'Me' keyword refers to that instance of the class (in the case you could have more than 1 instance of the class loaded).
ToInput 'The UserForm object is implied by it's own class.
The Me Keyword
Avoiding implicit qualifiers
To fix your problem, you will need to remove your naming conflicts. Either rename your variables or rename your textbox controls - *or both!
Consider doing a bit of research about good practices when it comes to naming variables, controls, objects, data types etc. I like to use descriptive names so someone who doesn't already know what the codes is doing can read it and understand - but this can come at the cost of making the code quite verbose - or - dense.
Consider chaning the names like this: (Note: It is entirely up to you how you name things in your code, this is just a suggestion.)
Module:
----------------------------------------
Public PublicToInputString As String
Public PublicCCInputString AS String
Sub EmailInput()
EmailIdForm.Show
MsgBox PublicToInputString
MsgBox PublicCCInputString
End Sub
-----------------------------------
Userform:
-----------------------------------
Private Sub OKBTN_Click()
PublicToInputString = Me.ToInputTextBox.Value
PublicCCInputString = Me.CCInputTextBox.Value
Unload Me 'Consider chaning to Me.Hide
End Sub
Now the code is clear and telling us what is what - Anyone could understand you are assigning a textbox value to a public variable string and following a naming convention like this also means you are far less likely to have naming conflicts - Again - Note: this is not a naming convention you must follow, merely a suggestion. Everyone has their own way that works for them.

VBA best practices for modules relative to modeless userforms

I came across this similar issue and read the replies: Modeless form that still pauses code execution
I have been attempting to apply in my own situation the suggestion provided by David Zemens. In my situation, I cannot seem to find an approach that incorporates Mr. Zemen's suggestion without also utilizing a GoTo.
I am wondering if there is a better or more elegant solution.
Here is an outline of what I am doing:
I have a UserForm with a Command Button that begins the code execution that will perform several actions on multiple Excel workbooks. As such, there are a number of blocks of code and the successful completion of one block of code allows for the execution of the subsequent block of code.
At a certain point, depending on the situation, the code might require User input; in other situations, the needed data is obtainable from an Excel. If input is needed from the User, another UserForm is displayed.
The User may need to view several different Excel sheets before entering the input, so the UserForm is modeless. So the code comes to a stop until the User enters the needed input and clicks another Command Button.
It is at this point I am having trouble: how to resume the program flow. Is the only way to 'pick-up where it left-off' is by using a GoTo statement? Or is there some way to organize the modules so there is a single consistent program flow, defined in one spot and not duplicated from the point at which User input might be needed?
Here is my take on the problem . Hope I understood the problem correctly.
Assumptions:
There are two user forms.
UserForm1 with a button to start the processing.
UserForm2 with a button to supply intermediate input.
A sub inside a module to start/ launch UserForm1.
VBA Code (for the sub routine)
Sub LaunchUserForm1()
Dim frm As New UserForm1
'/ Launch the main userform.
frm.Show vbModeless
End Sub
VBA Code (for UserForm1)
Private Sub cmdStart_Click()
Dim i As Long
Dim linc As Long
Dim bCancel As Boolean
Dim frm As UserForm2
'/ Prints 1 to 5 plus the value returned from UserForm2.
For i = 1 To 5
If i = 2 Then
Set frm = New UserForm2
'/ Launch supplementary form.
frm.Show vbModeless
'<< This is just a PoC. If you have large number of inputs, better way will be
' to create another prop such as Waiting(Boolean Type) and then manipulate it as and when User
' supplies valid input. Then validate the same in While loop>>
'/ Wait till we get the value from UserForm2.
'/ Or the User Cancels the Form with out any input.
Do While linc < 1 And (linc < 1 And bCancel = False)
linc = frm.Prop1
bCancel = frm.Cancel
DoEvents
Loop
Set frm = Nothing
End If
Debug.Print i + linc
Next
MsgBox "User Form1's ops finished."
End Sub
VBA Code (for UserForm2)
Dim m_Cancel As Boolean
Dim m_prop1 As Long
Public Property Let Prop1(lVal As Long)
m_prop1 = lVal
End Property
Public Property Get Prop1() As Long
Prop1 = m_prop1
End Property
Public Property Let Cancel(bVal As Boolean)
m_Cancel = bVal
End Property
Public Property Get Cancel() As Boolean
Cancel = m_Cancel
End Property
Private Sub cmdlinc_Click()
'/Set the Property Value to 10
Me.Prop1 = 10
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'/ Diasble X button
Me.Cancel = True
Me.Hide
Cancel = True
End Sub
OK so here are my thoughts.
You have a userform frmSelectUpdateSheet which you wish to use in order to allow the user to select the sheet, when the sheet can't be determined programmatically. The problem is that if you do .Show vbModeless (which allows the user to navigate the worksheet/s), then code continues to execute which either leads to errors or otherwise undesired output.
I think it's possible to adapt the method I described in the previous answer. However, that's out of the question here unless you're paying me to reverse engineer all of your code :P
Assuming you have a Worksheet object variable (or a string representing the sheet name, etc.) which needs to be assigned at this point (and that this variable is Public in scope), just use the CommandButton on the form to assign this based on the selected item in the frmSelectUpdateSheet list box.
This is probably a superior approach for a number of reasons (not the least of which is trying to avoid application redesign for this sort of fringe case), such as:
This keeps your form vbModal, and does prevent the user from inadvertently tampering with the worksheet during the process, etc.
Using this approach, the thread remains with the vbModal displayed frmSelectUpdateSheet, and you rely on the form's event procedures for control of process flow/code execution.
It should be easier (and hence, cheaper) to implement; whether you're doing it yourself or outsourcing it.
It should be easier (and hence, cheaper) to maintain.
NOW, on closer inspection, it looks like you're already doing this sort of approach with the cmdbtnSelect_Click event handler, which leads me to believe there's a related/follow-up problem:
The sheet names (in listbox) are not sufficient for user to identify the correct worksheet. So if the user needs the ability to "scroll" the sheet (e.g., to review data which does not fit in the window, etc.), then add some spinner buttons or other form controls to allow them to navigate the sheet.

String declaration error in if else

I have Camp as string. When I write this code, I get an error:
*Me.BoatDesc =< the expression you entered refer to an object that is close*
Here is my code
private Sub Save_Click()
Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
Correct me if I am wrong.
You are using VBA, not VB.Net. Here are some notes
Here is a simple form, it will be open when the code is run. The code will be run by clicking Save. Note that the default for an MS Access bound form is to save, so you might like to use a different name.
This is the form in design view, note that there is a control named BoatDesc and another named Amount, as can only be seen from the property sheet.
The save button have an [Event Procedure], which is the code.
Note that the code belongs to Form2, the form I am working with, and the words Option Explicit appear at the top. This means I cannot have unnamed variables, so it is much harder to get the names wrong.
This is the code to be run by the save button.
Option Compare Database
Option Explicit
Private Sub Save_Click()
''Do not mix up strings, variables, controls and fields
''If you want to know if a control, BoatDesc, equals
''the string "camp", you do not need this
''Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
End Sub

Disable button on a userForm

I'm trying to figure out how to disable a button within my userForm if a certain cell within my spreadsheet equals a certain number. I tried the code stated below, but it isn't working.
Private Sub UserForm_Initialize()
Label2 = Sheets("DATA").Range("AM2").Value
Label4 = Sheets("DATA").Range("AO2").Value
Label7 = Format(Sheets("DATA").Range("R8").Value, "Currency")
If Sheets("DATA").Range("AL10").Value = 10 Then
ActiveSheet.Shapes("CommandButton1").Select
UserFormact_Upgrade.CommandButton1.Enabled = False
Else
End If
End Sub
Your code should be working, as you're on the right path.
To test it, simply create a new form and add this code, you'll see it should work. Maybe you're having problems within the IF clause?
Besides, you don't need to select the shape prior to disabling it; just disable it right away.
Private Sub UserForm_Initialize()
CommandButton1.Enabled = False
End Sub
I know this is old, but got to this thread trying to solve my problem, and found a solution that wasn't mentioned here. So in case someone gets here like I did, and this didn't quite get them where they needed to go, I thought this might help.
I had a userform with a drop down box called cmdADAMFields, and I didn't want my submit button called FieldsSubmitButton to be enabled until I selected something from the dropdown box.
I had to break up my argument into two different private subs vs one larger If-Then-Else statement.
First, I put:
Private Sub UserForm_Activate()
If cmbADAMFields.ListIndex = -1 Then FieldsSubmitButton.Enabled = False
End Sub
Then when for my pulldown's private sub when it's value changed I wrote:
Private Sub cmbADAMFields_Change()
FieldsSubmitButton.Enabled = True
End Sub
The proper place for setting Enabled property is in Activate event (associated with Show method) and not Initialize event (associated with Load instruction).
The below code disable the button CommandButton1 when AL10 cell >= 10.
Private Sub UserForm_Activate()
CommandButton1.Enabled = ( Sheets("DATA").Range("AL10") < 10 )
End Sub
For buttons you can choose between normal buttons (property Enabled=False and property Visible=true), disabled buttons (property Enabled=False and property Visible=true) and invisible buttons (property Enabled=False and property Visible=False), that it is a cleaner interface, in most cases.
Concerning text boxes, besides normal, disabled and invisible status, there is a locked status, that is enabled and visible, but cannot be user edited. (property Locked = True)
A locked control only can be changed by VBA code. For instance, someone can includes date text boxes, that it's filled using a secondary popup date form with Calendar control.

Can't close userform

Let me set up the environment.
This is VBA code running in Excel.
I have a userform that contains a msflexgrid. This flexgrid shows a list of customers and the customer', salesperson, csr, mfg rep, and territories, assignments. When you click in a column, let's say under the Territory column, another userform opens to show a list of Territories. You then click on the territory of your choice, the userform disappears and the new territory takes the place of the old territory.
This all works great until you click on the territory of your choice the 'Territory' userform does not disappear (it flickers) and the new territory does not transfer the underlying userform.
I should mention that when I'm stepping through the code it works great.
I'm assuming it has something do to with the flexgrid as all the other userform (that don't have flexgrids) that open userform work just fine.
Following is the some code sample:
** Click event from flexgrid that shows Territory userform and assignment of new territory when territory userform is closed.
Private Sub FlexGrid_Customers_Click()
With FlexGrid_Customers
Select Case .Col
Case 0
Case 2
Case 4
Case 6
UserForm_Territories.Show
Case Else
End Select
If Len(Trim(Misc1)) > 0 Then
.TextMatrix(.Row, .Col) = Trim(Misc1)
.TextMatrix(.Row, .Col + 1) = Trim(Misc2)
End If
End With
End Sub
** The following Subs are used in the Territory userform
Private Sub UserForm_Activate()
Misc1 = ""
Misc2 = ""
ListBox_Territory.Clear
Module_Get.Territories
End Sub
Private Sub UserForm_Terminate()
Set UserForm_Territories = Nothing
End Sub
Private Sub ListBox_Territory_Click()
With ListBox_Territory
Misc1 = Trim(.List(.ListIndex, 0))
Misc2 = Trim(.List(.ListIndex, 1))
End With
Hide
UserForm_Terminate
End Sub
I know this a long winded explanation but I'm a fairly decent VBA programmer and this has me stumped.
Any help would be greatly appreciated.
I'm not going to say what you're doing is wrong (in that it won't ever work), but it scares the heck out of me. This is not the way I'd deal with forms.
Firstly, you're using UserForm_Territories (the class/form name) to refer to an implicitly-created instance of the form. This is something I've always avoided doing. I would always create an instance of a form explicitly, so instead of:
UserForm_Territories.Show
I would do:
Dim oTerritoriesForm As UserForm_Territories
Set oTerritoriesForm = New UserForm_Territories
oTerritoriesForm.Show vbModal
' get the values from the form here
Unload oTerritoriesForm
Next, and much more worryingly, you're subverting the UserForm_Terminate behaviour by calling it explicitly. Why you're doing this I can't imagine, unless you thought that it would work around your stated problem. My advice: don't do that.
Worse, you're attempting to assign to the implicitly-created instance of the form within that Terminate method. You shouldn't be doing that, either. I'm surprised that even compiles.
It seems like you're trying to force the implicitly-created instance of the form to mimic an explicitly-created one. In which case, create it explicitly, as shown above.

Resources