Adding two new command clicks to form by copying - excel

I added two facilities to a form. When I try to run it, it says no selection is made.
If I select "all", it runs. I can't run SMC or SCC, the two additions to the form, individually.
I created the two facility clicks by copying SGV and WHT and renaming.
I edited the coding to run SMC and SCC, that's why it works when I select all.
Private Sub OkayCommandButton_Click()
' Determine which facility is selected
Dim oCtl As MSForms.Control
On Error Resume Next
For Each oCtl In Me.SelectFacFrame.Controls
If TypeName(oCtl) = "OptionButton" And oCtl.Value = True Then
facOption = oCtl.Caption
Exit For
End If
Next oCtl
Debug.Print Err.Number
If Err.Number = 438 Then
MsgBox "No facility selection made. Try again."
Unload Me
End
End If
On Error GoTo 0
Unload Me
End Sub

An Option Button control has both a Name property and a Caption property. When you copied and pasted an old Option Button, you probably updated the Caption property but not the Name property. Your selection logic is based on the Caption property, but your action logic might use the Name property. Check the Name property for the two buttons you copied.

Related

In Excel, updating a formula seems to trigger an unrelated private sub on the same worksheet. How can I get this to stop?

I've created a drop down menu through data validation for workbook navigation. The following is a snippet of code I have for the drop down box to change worksheets in the workbook:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not (Application.Intersect(Range("J4"), Target) Is Nothing) Then _
ThisWorkbook.Sheets("Home").Visible = False
ThisWorkbook.Sheets(Target.Value).Activate
ThisWorkbook.Sheets(Target.Value).Visible = True
ThisWorkbook.Sheets("Imported Data").Visible = False
End Sub
The code is meant to hide all other worksheets that are accessible by the drop down list besides the one selected. I have about 10 tabs and this code has worked perfectly to achieve the basic goal of navigation. However, some pages have formulas and when you update data in the cells meant for calculations the workbook jumps to a random worksheet in the workbook that is not at all referenced in this sub.
Is there some way to have my worksheets not try to do anything with this sub unless the dropdown menu itself is changed?
Bonus (less important) Question: is there a way to make the drop box default to (blank) unless the menu itself is accessed?
Then _
The space followed by an underscore _ means that the current statement isn't finished yet but continues on the next line. Right now the last 3 lines will run whenever there is a change in the worksheet. Put the entire code in If-Endif.
Also avoid unnecessary use of On Error Resume Next. Use proper error handling.
You need to make the sheet visible before you activate it and not vice versa.
Try this
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
If Not (Application.Intersect(Range("J4"), Target) Is Nothing) Then
ThisWorkbook.Sheets("Home").Visible = False
ThisWorkbook.Sheets(Target.Value).Visible = True
ThisWorkbook.Sheets(Target.Value).Activate
ThisWorkbook.Sheets("Imported Data").Visible = False
End If
Letscontinue:
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
is there a way to make the drop box default to (blank) unless the menu itself is accessed?
If you have created it with Data Valdation then insert a blank value in the list.

Runtime Error 1004 / Error 438 when getting the Text property of a copied button, setting the property works though

I've inserted a large number of form controll buttons (with the text "") into an excel worksheet by copy'n'pasting (from another workbook).
These buttons are connected to this macro (which is located in PERSONAL.XLSB):
Option Explicit
Sub ChangeSomething()
' The button which called the macro.
Dim b As Button
Set b = ActiveSheet.Buttons(Application.Caller)
' Do run the code if the button was not already active.
If b.Text <> "x" Then ' SEEMS TO BE THE PROBLEM
' Do something
' Mark the button as activated.
b.Text = "x"
b.Font.Bold = True
' If the button was already activated, deactivate it.
Else
'Mark the button as deactivated.
b.Text = " "
End If
End Sub
This set up worked properly before. But since copying, I get Runtime Error 1004 "Unable to set the Text property of the Button class".
When handled, the exception seems to be Error 438 "Object Doesn't Support This Property or Method".
The Debugging marks the line:
If b.Text <> "x" Then
What puzzles me is that getting the text property seems to throw the runtime error, but setting the value runs just fine:
b.Text = "x"
The correct Button in the correct Worksheet of the correct Workbook is changed.
As soon as I change the text of the button manually to something other than "", the macro also seems to work.
Unfortunately, the inserted buttons do not appear to be included in the list returned by ActiveSheet.Buttons, so I can not loop over them to change their values.
I'm not sure if that's appropriate, but I've uploaded here a sample file.
I'm not sure your question has the right details. If these buttons are indeed Form Controls - and I think they must be because I believe ActiveX controls don't return a Caller - then their parent object is Shapes, and you would call the Text property from the Shape.TextFrame.Characters object.
I wonder if your original worksheet had aCollection object called Buttons
which contained, perhaps, a list of the buttons in the form of a class object, called Button (hence your error message) and which exposes the properties that are called in your code.
Without that collection and class, the code would be more like this:
Public Sub ChangeSomething()
Dim btn As Shape
With Application
'Check for correct data types.
If Not TypeOf .ActiveSheet Is Worksheet Then Exit Sub
If IsObject(.Caller) Then Exit Sub
'Acquire button
On Error Resume Next
Set btn = .ActiveSheet.Shapes(.Caller)
On Error GoTo 0
'Check for a found button
If btn Is Nothing Then Exit Sub
End With
'Check for non-actionable button.
With btn.TextFrame.Characters
If .Count = 0 Then
.Text = "x"
Exit Sub
End If
End With
'If we've reached here, it's an actionable button.
Debug.Print "Do something."
'Clear the button text.
btn.TextFrame.Characters.Text = ""
End Sub
I rather suspect some half-copied code here, and the rest of the project remains elsewhere.

Obtaining a single variable from a userform in Visual Basic

I have little VB knowledge but was hoping to figure this out without any help, but alas!
I have a spread sheet that shows the layout of HP blades inside a chassis. Currently I have set-up a macro (I right-clicked view code on the sheet) whereby you click on a specific 'blade' (really a few merged cells) and an inputbox appears, asking the user which IP they'd like - this then looks for exact matches (e.g. O - OAM, I - iLO) and depending on the option selected - assigns a value in a case statement. For example: O - OAM IP would assign the number 2 - which performs a VLOOKUP and looks in column 2 to return an IP address to the users clipboard.
I'm trying to change the inputbox to a userform. I've created one and have a simple dropdown with the available options.
I'm struggling to export the users selection into a variable to be used later on.
All of my efforts so far result in a variable being assigned following completion of the 'userform' and pressing the 'OK' button. But that variable is then not assigned in the sheet's code area. I assume it's something to do with scope and assignment.
Code for the worksheet
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, Range("D57:H80")) Is Nothing Then
Dim HostName As String
Dim ColIndex As Integer
Dim NodeIP As String
HostName = ActiveCell.Value
ufrmIPSelection.Show
'uOption = InputBox("Option Required" & vbCrLf & "o = OAM IP" & vbCrLf & "i = iLO IP", "User selection required")
'uOption
MsgBox "Option captured is " & uOption
Select Case uOption
Case "OAM IP"
ColIndex = 2
Case "iLO IP"
ColIndex = 3
End Select
MsgBox "Column referenced is " & ColIndex
NodeIP = Application.VLookup(HostName, Sheet7.Range("A1:C503"), ColIndex, False)
MsgBox "Hostname is " & HostName
MsgBox "Node IP is " & NodeIP
End If
End Sub
and code for the userform OK button
Public Sub cmdOK_Click()
uOption = cboIP.Value
MsgBox "Combo Box Value " & uOption
'Above line used to see if variable has been assigned
Unload Me
End Sub
Thanks,
Unload Me essentially says "destroy this object". But a form's default instance isn't just any other object - it's a global instance that's automatically re-created by VBA whenever it's referenced again, if it's ever destroyed. But the re-created instance can't magically remember what the previous state was: the re-created instance therefore has whatever the inital / design-time state was.
Another thing that destroys a form and its state, is the little [X] button in the top-right corner.
A modal form is essentially a dialog - and a dialog can either be accepted, or cancelled. When the user dismisses your dialog by clicking that little [X] button, they're telling your program that they wish to cancel whatever that dialog was intended to be doing.
For this reason, it's important that a dialog does nothing other than collect data from the user. The basic code-behind boilerplate for a UserForm with an Ok and a Cancel button, might look like this:
Option Explicit
Private cancelled As Boolean
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelled
End Property
Private Sub OkButton_Click()
Hide
End Sub
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
OnCancel
End If
End Sub
Private Sub OnCancel()
cancelled = True
Hide
End Sub
Handling the QueryClose event allows us to cancel the destruction of the form and the resetting of its state when the user clicks that [X] button.
Now the calling code can do this:
ufrmIPSelection.Show
If ufrmIPSelection.IsCancelled Then Exit Sub
But since that's storing the state in the form's global/default instance, it's a much better idea to do this:
With New ufrmIPSelection
.Show
If .IsCancelled Then Exit Sub
End With
That way every usage of the form is nicely insulated, and the form instance and its state only live as long as it needs to (i.e. it's gone at End With).
You could go ahead and query the individual controls' state from the calling code, but a much cleaner approach would be to encapsulate that state, using properties (a bit like suggested in 3.1415's answer); these properties only need to be read from the calling code, so only a Propery Get member is needed:
Public Property Get SelectedIPType() As String
SelectedIPType = cboIP.Value
End Property
And now the calling code can do this:
With New ufrmIPSelection
.Show
If .IsCancelled Then Exit Sub
MsgBox .SelectedIPType
End With
The next step would be to extract an actual stateful class to encapsulate the model, so as to completely decouple the form from the data.
Read more about forms' default instance in UserForm1.Show, an article I wrote a few months ago (the basic code-behind boilerplate snippet is taken from there).
You need to add a public property to your form ...
Private m_OptionChoice as String
Property Let OptionChoice(value As String)
m_OptionChoice = value
End Property
Property Get OptionChoice() As String
OptionChoice = m_OptionChoice
End Property
And then change your OK button code to ...
Public Sub cmdOK_Click()
OptionChoice = cboIP.Value
MsgBox "Combo Box Value " & uOption
'Above line used to see if variable has been assigned
Unload Me
End Sub
Now when your form closes, you can access the OptionChoice via ufrmIPSelection.OptionChoice. Eg change ...
MsgBox "Option captured is " & uOption
to ...
MsgBox "Option captured is " & ufrmIPSelection.OptionChoice
Finally, as suggested in Mathieu Guindon's comment below, you need to replace Unload Me with Me.Hide. Thanks Mathieu!

UserForm RefEdit - Code to reinitialize range selection

I have created a Userform with several option buttons as part of a larger macro. The UserForm will load if there is a specific calculation error and will ask the user to select a method to correct the error. One option enables a RefEdit control and allows the user to select a new starting cell (and skip the errors and cells between the current and newly defined range).
I have used the _Exit event to set up some error checking (e.g. to ensure a valid range is selected or to ensure the range is a 1x1 range), but I have been unable to find a way to force the RefEdit control to "reinitialize." I have tried using the RefEdit.SetFocus method but this is not producing the result I want.
Basically, is there a command I can use that mirrors the act of clicking the dropbutton on the RefEdit control?
Private Sub RefEdit_NewStartCell_Exit(ByVal Cancel As MSForms.ReturnBoolean)
On Error Resume Next
Set UserRange = Range(RefEdit_NewStartCell.Text)
If Err.Number <> 0 Then
MsgBox "Invalid range selected"
RefEdit_NewStartCell.SetFocus
End If
On Error GoTo 0
This is not a "real" answer, but a workaround (most times excel crashed using a RefEdit, so i could not run tests... sorry)
Do it with your RefEdit if u want (i have used a textbox):
Private Sub TextBox1_Enter()
On Error Resume Next
Set UserRange = Nothing
UserForm1.Hide
While UserRange.Count <> 1
Set UserRange = Application.InputBox("Select Range", , , , , , , 8)
Wend
CommandButton1.SetFocus
TextBox1.Value = UserRange.Address
UserForm1.Show
End Sub
Hopefully someone got a better answer soon ^.^;
At least the inputbox will only return a valid range (no need to check that)

Validate VBA drop down on click

I have a drop down select list in a VBA form I would like to validate as soon the user clicks on it. It needs to check that a pre-requisite drop down has been filled already.
This is to avoid the user jumping ahead on the form because there are certain fields that need to be filled out first. My attempt so far is not working:
Private Sub cbo_moduleName_Click()
If Len(cbo_moduleCode.Value) = 0 Then
MsgBox ("Please select a module code")
Exit Sub
End If
End Sub
It seems the Click event is activated only when the box's value is changed with the mouse, not every time it is physically clicked on. Try this:
Private Sub cbo_moduleName_Enter()
If Len(cbo_moduleCode.Value) = 0 Then
MsgBox ("Please select a module code")
cbo_moduleCode.SetFocus
Exit Sub
End If
End Sub

Resources