I've got some code where the user has to multi-select items from a 2-column listbox. These items are variables which will later be plotted, and each have their own units (i.e. °C, °F, etc.). If the user selects items which have different units, an error message appears telling them to reselect.
The problem I'm having is that after the user reselects, it seems the initial selections are still there, because when the plot is generated I can see those variables being plotted. Here's what I've tried without any success:
If InStr(header, yUnit) = 0 Then 'this is the check to see if the selections use different units
MsgBox "Error! y variable selections contain different units. Please choose again"
Application.DisplayAlerts = False
ActiveChart.Delete 'Delete the chart which is currently being constructed
Application.DisplayAlerts = True
'Method 1:
Unload FormatChart
FormatChart.UserForm_Initialize 'FormatChart is the form in which the listbox is contained.
'UserForm_Initialize is the subroutine which constructs the listbox
FormatChart.Show
'Method 2:
With FormatChart.ListBox1
For x = 0 To .ListCount - 1
If .Selected(x) Then
.Selected(x) = False
End If
Next x
End With
FormatChart.Show
End If
'''
Solved it! I just have to use the following:
FormatChart.Show
Exit Sub
I believe what was happening was that the subroutine would continue to run, even though after I showed the form again I had an "Enter" button which would call the subroutine again, possibly resulting in it grabbing variables from the previous, still active, instance of it, or that multiple instances of that subroutine were running, not sure... anyway I'm just happy to end this headache :)
Related
I'm using an Advanced Filter, among others, with 0 and 1's. The filter works correctly only if in the Data sheet the cells with 1 and 0 have the error message "Number stored as text" displayed. For that I have to manually open the cell with double click and press Enter. Then the error message appears and the filter works. If I don't do that, the filter doesn't work.
Alternatively I can click on the cell with 1 or 0 and press F2 to display the error message.
Is there a way with VBA that I can automatically do that?
Many thanks for your help!
You'll find that buried deep in the Range properties for the cells, specifically in the Errors collection. Just find the cells where that error is present, then set the Ignore property to True:
Public Sub IgnoreNumsAsText()
Dim current As Range
For Each current In ActiveSheet.UsedRange.Cells
With current
If .Errors.Item(xlNumberAsText).Value = True Then
.Errors.Item(xlNumberAsText).Ignore = True
End If
End With
Next current
End Sub
VBA Newbie here.
I have searched high and low for this answer, and even come across other questions very similar to mine, but cannot get an answer. So I am hoping that this is my lucky day.
I have a Userform in excel that has Four combo boxes. Each combo box has a drop down with several choices. In two of these boxes, there are many business names and a lot of these names are similar. I was wanting to have the feature where are the data was being typed into the box, it would begin to narrow the options. EXAMPLE: if I type "heating and air" it begins to only show items in the list that include that word or phrase.
Is this a properties change in the box, or a code written, or something else?
Please help- I am stumped and no one seems to have the answer.
Very grateful-
Excel Newbie
Yes this is very possible. All you have to do is create a sub that populates that combo box, set it up so that when adding it checks the value of the box for example if it was the basic typing example. basic format. This assumes the possible values are stored in an array. This would add any item that has the string entered in it. (in any position)
For I = 0 to Number of Values
If instr(Value(I), ComboBox.Text) > 0 then
add item
endif
next
I played around a bit and came up with something to get you started. This basically functions as an "auto search". It is not autocomplete because it will search entire terms, not just terms which begin with whatever you've typed in. Basically I assume you have a range of cells (in this example cells A2:A121) that have the date for your drop down in it.
Setup
Add a new generic module (I named mine GlobalVars and add the following code:
Option Explicit
Public IgnoreChange As Boolean
Public RangeOfData As Variant
The Code
Open the code to your UserForm.
Add the following code:
Private Sub UserForm_Initialize()
RangeOfData = Application.WorksheetFunction.Transpose(Sheet1.Range("A2:A121").Value)
IgnoreChange = False
End Sub
Be sure to update A2:A121 and Sheet1 (I am using code name, but Worksheets("Sheet1") would work just as well) to point to the data which contains your combobox choices.
Now, the meat of the job is handled in the Combobox_Change event
Private Sub ComboBox1_Change()
If Me.ComboBox1.Text = vbNullString Then
Me.ComboBox1.Clear
SendKeys ("{Enter}")
End If
If Me.ComboBox1.TextLength > 2 Then
Dim i As Long, j As Long
If IgnoreChange = False Then
Me.ComboBox1.Clear
SendKeys ("{Enter}")
DoEvents 'Bug with NumLock
For i = LBound(RangeOfData) To UBound(RangeOfData)
If UCase(RangeOfData(i)) Like "*" & UCase(Me.ComboBox1.Text) & "*" Then
Me.ComboBox1.AddItem RangeOfData(i)
End If
Next i
Me.ComboBox1.DropDown
End If
End If
IgnoreChange = False
End Sub
Be sure to change ComboBox1 to the name of your combobox control.
Basically, what this does is it handles user input when it reaches two characters or longer. The code will search through your input data range and then return results that match the string as the user is entering it. The results is like a suggestions box.
I should note that with this method, the combobox is NOT pre-populated with data, so users must begin typing something into the combobox.
Additionally, I added the following code to handle the backspace key:
Private Sub ComboBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = 8 Then
IgnoreChange = True
End If
End Sub
Again be sure to change ComboBox1 as appropriate.
For my example, I loaded all 120 crayola crayon colors into a spreadsheet (that is what is in Sheet1 from A2:A121).
Here is example output for when I start typing, first I input blu:
As you can see I am getting all values that contain blu, including those that don't start with blu such as Cadet Blue or Midnight blue.
As another example, I will search for flower
So as you can see, instead of being a combobox with 120 static options, it is updated based on what the user types and tied to a list of values in your cells.
I did notice that SendKeys sometimes toggled my NumLock, this is a known issue. The point of that line is to collapse the drop down after the user deletes all of the text or continues texting so as to "refresh" the auto-generated list.
All:
Thank you in advance, you all have been a tremendous resource!!!
I have a couple of spreadsheets where the sheet is protected, but users can still use filters. I'm processing most of the sheets automatically, but what I need to do, is present the user with the sheets that need to be filtered, then have them select a "Finish" type button or toolbar entry, which I already have.
What I need to be able to do, is to bring this sheet up, pause the macro, if possible, while they make their changes (could be up to 5 filters that they select before the sheet is ready.
Then, copy the visible cells only to a specific sheet and then resume the macro.
I don't think that Worksheet change event will do this.
I'm thinking more on the lines of maybe setting a flag on a spare sheet, firing up the next macro and then see if it can find the original macro and pick up where it is flagged?
I thought about a modeless userform that the user could click OK on and then call the next macro, but that does not work.
The calling code is:
UserForm3.Show
CopyToDisplay "AEP"
LastPos = LastPos + 1
Where AEP is the sheet name to copy the filtered rows from.
Userform displays, but clicking ok does nothing and of course, the macro keeps on going.
Any suggestions would be greatly appreciated!
Thanks,
Jeff
Jeff let's try this. Your current code:
UserForm3.Show
CopyToDisplay "AEP"
LastPos = LastPos + 1
When we display a UserForm, the default behavior is vbModal, which essentially freezes the application and the user cannot interact with anything but the UserForm, that is not what you want. What you need is a way to display the form, and then just wait for the user to signal that s/he is finished with the input.
So we need to modify a few things:
The UserForm needs to effectively "pause" while also allowing the user to interact with the worksheet. A vbModal form can't do this (it pauses, without interaction), and really neither can a vbModeless (it continues execution AND allows interaction).
Conundrum? No. we can simulate a pause with the vbModeless form, and preserve the user's ability to interact with the sheet. The best of both worlds!!
We will show the form with the optional vbModeless, this allows the user to interact with the rest of the Application /worksheets/etc. Since a modeless form continues code execution, we need to simulate a pause and we can do this with a Loop. The loop will run indefinitely, and only break once the UserForm is closed, at which point the rest of the code will continue to execute.
UserForm3.Show vbModeless
Do While UserForm3.Visible
DoEvents
Loop
LastPos = LastPos + 1
'You MAY need to reset some variables, if the Filter/Autofilter has affected these/etc.
Design-wise, give your form a single Label control and set its .Caption property (and the form's .Caption property) in some useful/instructive way. You could add a command button but that seems unnecessary, since all the button would do is invoke the Terminate event (which can always be done with the red "X")
For your issue with copying (apparent failure to paste), try changing this:
Sheets("AEP").Select
With ActiveSheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy _
Destination:=Sheets("Display").range("A" & LastPos)
.AutoFilterMode = False
Application.CutCopyMode = False
End With
To this:
With ActiveSheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy
Sheets("Display").range("A" & LastPos).PasteSpecial
.AutoFilterMode = False
Application.CutCopyMode = False
End With
I am trying to build a main page for my costing model. On this page I have created a drop down list using a combo box and then I want to assign a macro that creates a list of different buttons/command buttons once an option is selected from the list. Then I want to build another macro that is assigned to those buttons which then take the user to another tab/sheet within the same workbook depending on the option they selected from the drop down.
Can someone please give me an idea as to what code I should be using, first to create a command button that refers to the selected option from the drop down and then assign a simple macro to that button which then takes me to the specified tab/sheet?
So far, I have got the following:
Option Explicit
Sub Select_Change()
With ThisWorkbook.Sheets("Main Page").Shapes("Select").ControlFormat
Select Case .List(.Value)
Case "Vehicle1": All_States1
Case "Vehicle2": All_States2
Case "Vehicle3": All_States3
Case "Vehicle4": All_States4
Case "Vehicle5": All_States5
Case "Vehicle6": All_States6
Case "Vehicle7": All_States7
End Select
End With
End Sub
I then tried to use the name All_States1 to create various buttons but it's not working properly, as all selected options are showing the same button and the button won't go away either. Also, I can't seem to assign a macro to the created button.
This is just an example of :
creating a Button
assigning a macro to it
.
Sub button_maker()
Dim r As Range
Set r = Selection
ActiveSheet.Buttons.Add(94.5, 75.75, 51, 27.75).Select
With Selection
.OnAction = "mooney"
.Characters.Text = "Bump"
End With
r.Select
End Sub
Sub mooney()
Range("A1").Value = Range("A1").Value + 3
End Sub
If I understand the problem correctly, you want to have a dropdown (combo box) on your sheet, and when a button is clicked you want to run a macro based on the selection. The following does that - see if it helps you.
First - create a combobox, and a range for the inputs (names in the combobox) and output (value selected). For example, you could call the input selectionIn and the result selectionOut.
Exact steps:
Wrote values of combobox selections in E1:E4 . Selected the four cells, then typed selectionIn in the name box (to the left of the formula bar). That creates a named range (there are other ways to create named ranges, but this is my preferred method).
Called cell F1 selectionOut
Created a combobox, and referenced these two ranges for its input and output:
Created a button, gave it the label "Go" and linked it to the action runIt.
Finally, I created the following code in a workbook module:
Sub runIt()
Dim whatToDo, makeName As Boolean
' look up the name of the combo based on the value:
whatToDo = Range("selectionIn").Cells([selectionOut].Value, 1)
MsgBox "have to do '" & whatToDo & "'"
makeName = False
Select Case whatToDo
Case "one"
' example of putting the code you need right in the select:
MsgBox "doing the first thing"
Case "two"
' example of calling a specific routine:
Call caseTwo
Case "three"
Application.Run "case" & whatToDo ' making the name of the function on the fly
Case "four"
makeName = True
End Select
If makeName Then
Dim nameToRun
nameToRun = "case" & whatToDo
Application.Run nameToRun
End If
End Sub
Sub caseTwo()
MsgBox "called the code for case two"
End Sub
Sub caseThree()
MsgBox "doing case three here"
End Sub
Sub caseFour()
MsgBox "even four can be done"
End Sub
This shows a few different ways to handle different cases depending on what was selected. Of course you can have a macro run every time the combobox selection is changed - but it sounds from your description like that is not what you want.
Let me know how you get on with this code example - I tried to keep it simple but show some options at the same time.
One alternative (which might be simpler) would be to have an array with the names of the functions that you want to call:
Sub otherMethod()
Dim functionList()
functionList = Array("caseOne", "caseTwo", "caseThree", "caseFour")
Application.Run functionList([selectionOut].Value - 1)
End Sub
That's certainly the most compact way I can think of to do this... you need the offset of -1 because the array index is base 0 (by default anyway) and the combobox returns 1 for the first selection. You could make your code more robust by writing
functionIndex = [selectionOut].Value + LBound(functionList) - 1
Application.Run functionList(functionIndex)
This ensures that if you change the base index of the functionList array to another value, it will all still work correctly.
I couldn't observe the number of rows completed during run time. Hence I was thinking why not just come out few line of codes to display the number of rows completed in run time.
Follow are my implementation:
Dim ownCntrl As Control
Set ownCntrl = UserForm1.Controls.Add("Forms.TextBox.1")
With ownCntrl
.Name = "RowNoTextBox"
.Value = "test"
.Width = 150
.Height = 25
.Top = 10
.Left = 10
.ZOrder (0)
End With
UserForm1.Show
Following are my queries:
1)Beside TextBox Object, is there any other Object like label in Java to display information in VBA?
2)Without Created a UserForm called "UserForm1" initially, can I create the TextBox Object or other suitable Object Directly during run time? Cause, without called the UserForm1.Show function, the TextBox Object won't display
3)How can I remain the UserForm or TextBox to display during run time without users' interaction to close it to proceed?
I'm just start to learn VBA. Appreciate you guys' suggestions. Thanks a lot.
According to some additional information placed in comments the simplest idea to keep information about progress of your subroutine is to use Excel status bar. Therefore we put within our subroutine something like this:
Application.StatusBar = "Current progress- Row No.: " & rowNum
where rowNum is a variable (loop variable) representing current row.
Which is important- you need to return standard status bar behaviour calling this line before end of your procedure:
Application.StatusBar = False