I currently have a userform that has a combobox and a listbox. Both contain the same list of items (the combobox has an extra null value). If a user selects an item in the combobox, then the same item will be selected in the list box.
The problem that I am having is coming from the combobox autocompleting when the user tries typing a value instead of choosing one from the combobox. When the user types a value, it will autocomplete what they typed to a value within the combobox. (If I type "8" then the combobox will autocomplete that to "8184123".)
If I set MatchEntry to 2 - fmMatchEntryNone, then the combobox does not autocomplete. However, the combobox does not select a value based on what the user has typed.
Is there any way to stop the combobox from autocompleting while keeping letting MatchEntry stay at 1 - fmMatchEntryComplete? Or is there anyway to implement fmMatchEntryComplete only when the value that the user enters is exactly equal to a value in the list of the combobox?
you can have ComboBox methods and properties work for you
in the form calling sub place:
Sub main()
' code that preceeeds the userform loading...
With UserForm1
'other code to set some userform or userform controls properties...
.ComboBox1.MatchEntry = fmMatchEntryNone ' <--| set this just before showing userform
.Show
End With
Unload UserForm1
' code that follows the userform closing...
End Sub
in the userform code pane place this function:
Function CheckCB(cboBox As MSForms.ComboBox) As Boolean
With cboBox
.Text = .Value '<-- This is the "trick": "refresh" the combobox text
CheckCB = .MatchFound
If Not CheckCB Then
MsgBox "Invalid entry", vbCritical
.SetFocus
.SelStart = 0
.SelLength = Len(.Text)
End If
End With
End Function
despite MSDN online doc the refreshing of .Value actually has MatchEntry and MatchRequired work on it even if MatchEntry is set to fmMatchEntryNone
then you have to call CheckCB() function to prevent leaving userform until your combobox has been entered a valid value
for instance you could place it in any "exit" button click event handler
Private Sub CommandButton1_Click()
If Not CheckCB( ComboBox1 ) Then Exit Sub '<-- if ComboBox check failed then exit
' otherwise let code run ...
End Sub
or, if you wanted the user not to enter any other control until your combobox has a valid value, you must act similarly for every other userform control event handler, i.e. placing If Not CheckCB Then Exit Sub at their beginning
Try using Form1.ComboBox1.AutoWordSelect = True
Custom Autocomplete
Turn off match entry
ComboBox1.MatchEntry = fmMatchEntryNone
When the user types iterate over the combo's list. It there is only 1 possible match, select it.
Private Sub ComboBox1_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Dim i As Integer, matchIndex As Integer
matchIndex = -1
For i = 0 To ComboBox1.ListCount - 1
If InStr(1, ComboBox1.List(i), ComboBox1.Value) Then
If matchIndex = -1 Then
matchIndex = i
Else
Exit Sub
End If
End If
Next
If matchIndex > -1 Then ComboBox1.ListIndex = matchIndex
End Sub
Related
I am working on a userform which contains numerous (an unknown number) of textbox controls. The number of textboxes on the form is based on certain parameters being met elsewhere within the project.
The number of textboxes on the form could get up to 100 or so and my users are expected to fill in each of them. However, in many cases the value for one textbox will be the same as the textbox positioned directly above it.
I therefore wish to implement functionality to allow the user to enter a "." followed by tabbing out of the field in order to copy the value from the textbox above it.
There are a couple of ways I could go about this initial requirement:
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2.Value = "." Then
TextBox2.Value = TextBox1.Value
End If
End Sub
This works absolutely fine.. however given that there is no indication here that the textbox2 is below textbox1 (Other than me knowing where I put it) we could do something like:
Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2.Left = TextBox1.Left And TextBox2.Value = "." Then
TextBox2.Value = TextBox1.Value
End If
End Sub
And again this works absolutely fine.
My problem is this, with up to 100 textbox controls I do not particularly want to write and maintain an Exit event for each possible textbox control.
I will probably phrase this incorrectly, but is there a way to trigger the exit event dynamically by passing the name of the textbox the user has tabbed out of?
Or am I lumbered within dozens of identical subs handling the exit of each textbox specifically?
EDIT:
Reading the post linked from Word Nerd, I have the following included with the userform initialize event
Dim tbCollection As Collection
Private Sub UserForm_Initialize()
Dim Ctrl As MSForms.Control
Dim obj As clsTextBox
Set tbCollection = New Collection
For Each Ctrl In usfEnterTime.Controls
If TypeOf Ctrl Is MSForms.TextBox Then
Set obj = New clsTextBox
Set obj.Control = Ctrl
tbCollection.Add obj
End If
Next Ctrl
Set obj = Nothing
Then we have the class module clsTextBox
Private WithEvents MyTextBox As MSForms.TextBox
Public Property Set Control(tb As MSForms.TextBox)
Set MyTextBox = tb
End Property
Private Sub MyTextBox_Change()
If MyTextBox.Value = ". " Then
If MyTextBox.Top = 24 Then
MsgBox "Nothing to copy from"
MyTextBox.Value = ""
Exit Sub
Else
For Each Ctrl In usfEnterTime.Controls
If Ctrl.Left = MyTextBox.Left And Ctrl.Top = MyTextBox.Top - 18 Then
MyTextBox.Value = Ctrl.Value
Application.SendKeys "{TAB}"
Exit Sub
End If
Next Ctrl
End If
Else
Exit Sub
End If
End Sub
My initial requirement was to use the EXIT event to implement a "." + TAB out of textbox to copy from above, however it appears the TextBox control does not have the EXIT event available. So I am using the change event, application.sendkeys and expecting my users to use ". " (Note blank space) to mimic the same functionality and tab to the next textbox in the tab order index using the spacebar instead. As you can see I am simply looping through all controls and checking the relative TOP and LEFT positions to determine which textbox to copy from. 24 is the position of the top most textbox control and so I am capturing this and displaying a msgbox to advise the user there will be nothing to copy from. -18 represents the gap between the tops of two textbox controls.
Working perfectly, thanks to Word Nerd for linking through to the other post.
Please note this question is related to Outlook
I am an amateurish programmer in VB in outlook
I want to have a custom Message Box with button captions as 'Send Anyway' and 'Don't Send'.
But with the existing message box changing text is not possible.
So I made a custom form. Now I want to return a Boolean value from the CommandButton1_Click() Sub
This is my main sub which call the form:
Public Result1 As Boolean
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Const MAX_ITEM_SIZE As Long = 5242880
Result1 = True
Dim FileSize As Long
For Each Item In Item.Attachments
FileSize = FileSize + Item.Size
Next
If FileSize > MAX_ITEM_SIZE Then
UserForm1.Show
'Cancel = True
Cancel = Result1
End If
End Sub
This is my code for click event handler:
Private Sub CommandButton1_Click()
Unload Me
End Sub
Please advise on how to achieve custom captions on MsgBox Buttons in Outlook
I'll use the Tag property of the UserForm object to pass back a value from it to its calling sub
this means you'll want to use UserForm Hide() method rather then Unload it from within its code pane, not to loose its state, i.e. all its properties values (and methods calling),
So I'd go like follows:
give meaningful names to your Userform1 button
for instance, let's rename
SendBtn, the button that has the "Send Anyway" caption
DoNotSendBtn, the button that has the "Don't Send" caption
you can actually use whatever name you want (even CommandButton1 and CommandButton2 would do), but be consistent with chosen names for their corresponding event handlers names
assign them the following click event handlers
Private Sub DoNotSendBtn_Click() '<--| change "DoNotSendBtn" to your actual chosen button name
Me.Tag = "True" '<--| store in userform 'Tag' property the value that will be read to cancel the email sending
Me.Hide '<-- this will hide the userform, thus not loosing its "state" -> 'Tag' property will still be available to the calling sub
End Sub
Private Sub SendBtn_Click()'<--| change "SendBtn" to your actual chosen button name
Me.Tag = "False" '<--| store in userform 'Tag' property the value that will be read to let the email sending go on its way
Me.Hide '<-- this will hide the userform, thus not loosing its "state" -> 'Tag' property will still be available to the calling sub
End Sub
finally, change your ItemSend event handler like follows
Option Explicit
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
Const MAX_ITEM_SIZE As Long = 5242880
Dim FileSize As Long
For Each Item In Item.Attachments
FileSize = FileSize + Item.Size
Next
If FileSize > MAX_ITEM_SIZE Then
UserForm1.Show '<--| show the userform
Cancel = UserForm1.Tag = "True" '<--| 'Cancel' will be set to 'True' if the userform TAG property value is "True", otherwise it'll be set to 'False'
Unload UserForm1 '<--| now unload the Userform (and loose its "state", which you don't need any more)
End If
End Sub
I have a workbook with about 130 sheets in it, which I select from a combobox on the main page. I populate the combobox using rowsource, from a named range which has the sheet names in it. It works great if you select one of the sheet names from the list, and it closes nicely if you don't enter anything and still press submit. I want to cover the case where the user types in something not on the list. I want to say something to VBA like 'if sWs is not equal to something from the named range, then show a message box which says 'error'', or whatever. Here is my code below, with my notes:
'if submit button is pressed go to selected worksheet or close the userform:
Private Sub Submit_Click()
Dim sWs As String
'the string is the text from combobox, called 'Title', selected on front page:
sWs = Me.Title.Value
'if nothing is selected then close the userform:
If sWs = ("") Then
End
End If
'If an item is selected from the dropdown list then show that worksheet and close the userform:
Worksheets(sWs).Select
End
End Sub
'If exit button is pressed close the userform:
Private Sub ExitButton_Click()
End
End Sub
I think I need to be using a Boolean, so I can specify what to do when it is false, but not sure how to write it. Sorry, I'm a bit of a noob.
Any ideas?
Try checking the list index of the item selected, -1 would indicate something has been entered that is not valid
'if submit button is pressed go to selected worksheet or close the userform:
Private Sub Submit_Click()
Dim sWs As String
'the string is the text from combobox, called 'Title', selected on front page:
If Me.Title.ListIndex = - 1 Then
MsgBox "Invalid Entry"
End
End If
sWs = Me.Title.Value
'if nothing is selected then close the userform:
If sWs = ("") Then
End
End If
'If an item is selected from the dropdown list then show that worksheet and close the userform:
Worksheets(sWs).Select
End
End Sub
'If exit button is pressed close the userform:
Private Sub ExitButton_Click()
End
End Sub
I have got a piece of code that works which changes a input field from disabled to enabled and changes the colour from gray to white if the corresponsding checkbox is ticked.
Is there a way to loop this or call it for all checkboxes & input fields without having a seperate piece of code for each pair?
The code I have is:
Private Sub CheckBox1_Click()
If CheckBox1.Value = True Then
tb01.Enabled = True
tb01.BackColor = vbWhite
Else: tb01.Enabled = False
tb01.BackColor = vb3DLight
End If
End Sub
Private Sub CheckBox2_Click()
If CheckBox2.Value = True Then
tb02.Enabled = True
tb02.BackColor = vbWhite
Else: tb02.Enabled = False
tb02.BackColor = vb3DLight
End If
End Sub
edit: This code is in a UserForm
Checkboxes are a collection in Excel, you can do something like this:
Sub SelectCheckboxes()
Dim CB As CheckBox
For Each CB In Sheet1
If CB.Name <> Sheet1.CheckBoxes("SkipThisCheckbox").Name Then
CB.Value = 2
End If
Next CB
End Sub
You change the value to all checkboxes, except the "SkipThisCheckbox" checkbox.
Edit: The idea is that the checkboxes are a collection and I have translated your question as "show me how to select/check checkboxes together and not one by one".
On a form it should be something like this:
Private Sub CommandButton1_Click()
Dim cCont As Control
For Each cCont In Me.Controls
If TypeName(cCont) = "Checkbox" Then
Debug.Print cCont
'True or False if checked
'Write your logic here.
End If
Next cCont
End Sub
the more versatile approach is a module class one
here follows a possible application to your case, assuming that TextBoxes and CheckBoxes in your UserForm are paired on a "Name" property basis like "CheckBox1"-"TextBox1", "CheckBox2"-"TextBox2", ...
in the UserForm code pane place the following code
Option Explicit
Dim chkBoxes(1 To 4) As ChkBox_TxtBox_Class 'array of type "ChkBoxClass" which you define in a Class Module
Private Sub UserForm_Initialize()
Dim nControls As Integer, i As Integer
nControls = 4 '<== set here the number of CheckBox-TextBox pairs you have in the Form
For i = 1 To nControls
Set chkBoxes(i) = New ChkBox_TxtBox_Class 'initialize a new instance of 'ChkBoxClass' class and store it in the array i-th position
With chkBoxes(i)
Set .ChkBox = Me.Controls("CheckBox" & i) 'assign the correct CheckBox control to its ChkBox property
Set .TxtBox = Me.Controls("TextBox" & i) 'assign the correct TextBox control to its TxtBox property
.ChkBox.value = False 'set "unchecked" as checkbox initial value
.SetTexBox ' call the class method that will have the "paired" textbox adjust its state accordingly to checkbox value
End With
Next i
End Sub
add a "Class Module" to your project
either clicking Insert-> Class Module in the VBA IDE main Ribbon menu
or right-clicking anywhere in the VBA IDE Project Window and selecting Insert -> Class Module in subsequent sub-menus
expand the "Class Module" node in the Project Window
if you don't see the Project Window you can open it by clicking View-> Project Window in the main ribbon menu, or press "Ctrl+R"
select the new Class you added (it should be some "Class1" or the likes) and change its name to "ChkBox_TxtBox_Class" in the Property Window "Name" textbox
if you don't see the Property Window you can open it by clicking View-> Property Window in the main ribbon menu or press "F4"
in the Class Module code pane place the following
Option Explicit
'declare class properties of CheckBox and TextBox type to be "paired" in every instance of this class. the CheckBox one will be associated to events
Public WithEvents ChkBox As MSForms.CheckBox ' ChkBox is a property of the class of type CheckBox. it's associated to events
Public TxtBox As MSForms.TextBox ' TxtBox is a property of the class of type TextBox
' events associated to ChkBox class property
Sub ChkBox_Change()
Call SetTexBox 'call the "method" (i.e. a Sub attached to the class) associated to ChkBox property state change
End Sub
Sub SetTexBox()
'sub that sets the state of the associated TextBox accordingly to the state of the associated CheckBox
With Me.ChkBox
If .value Then
TxtBox.Enabled = True
TxtBox.BackColor = vbWhite
Else
TxtBox.Enabled = False
TxtBox.BackColor = vb3DLight
End If
End With
End Sub
run your routine
I have a userform with a multiple frames, all filled with multiple checkboxes. I've named the checkboxes to their corresponding Excel cells. Now I want to make VBA run on clicking any of these checkboxes on run time. I know I can do this by creating a click-sub for every individual checkbox, but there must be a cleaner way to do this.
So far I've tried to put this code in the userform_Click and userform_Mousedown events, but they don't run when I click the checkboxes. Does anyone have an idea how to do this?
Dim iControl As Control
For Each iControl In Me.Controls
If TypeName(iControl) = "CheckBox" Then
If iControl.Value = True And Range(iControl.Name).Value = "" Then
Range(iControl.Name).Value = Format(Now, "dd.mm.yyyy")
ElseIf iControl.Value = True And Range(iControl.Name).Font.Color = vbWhite Then
Range(iControl.Name).Font.Color = vbBlack
ElseIf iControl.Value = False And Range(iControl.Name).Value <> "" Then
Range(iControl.Name).Font.Color = vbWhite
End If
End If
Next
As SilentRevolution said - you need an event to fire when you click the button. You're just after a single procedure to fire all check box click events.
So:
Create a class module called cls_ChkBox.
In the class module you'll add the Click event code:
Option Explicit
Private WithEvents chkBox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set chkBox = ctrl
End Sub
Private Sub chkBox_Click()
ThisWorkbook.Worksheets("Sheet1").Range(chkBox.Name).Value = Format(Now, "dd.mm.yyyy")
End Sub
Now you just need to attach the chkBox_Click event to each check box on your form. In your user form add this code:
Option Explicit
Private colTickBoxes As Collection
Private Sub UserForm_Initialize()
Dim ChkBoxes As cls_ChkBox
Dim ctrl As Control
Set colTickBoxes = New Collection
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" Then
Set ChkBoxes = New cls_ChkBox
ChkBoxes.AssignClicks ctrl
colTickBoxes.Add ChkBoxes
End If
Next ctrl
End Sub
Each check box is given its own instance of the class, which is stored in the colTickBoxes collection.
Open the form and the cell in Sheet1 will update to show the date depending on the name of the check box.
You need an event to run code, if there is no event, the macro cannot start. I don't know if there is a single event that triggers for any button or checkbox that is clicked.
If the code you want to execute is the same every time except the control, you could write a private sub in the userform module which is called for each event, the private sub can take an input between the () for example.
Private Sub CheckBox1_Click()
Call ExecuteCode(Me.CheckBox1)
End Sub
Private Sub CheckBox2_Click()
Call ExecuteCode(Me.CheckBox2)
End Sub
Private Sub CheckBox3_Click()
Call ExecuteCode(Me.CheckBox2)
End Sub
Private Sub ExecuteCode(IControl As Control)
If TypeName(IControl) = "CheckBox" Then
If IControl.Value = True And Range(IControl.Name).Value = "" Then
Range(IControl.Name).Value = Format(Now, "dd.mm.yyyy")
ElseIf IControl.Value = True And Range(IControl.Name).Font.Color = vbWhite Then
Range(IControl.Name).Font.Color = vbBlack
ElseIf IControl.Value = False And Range(IControl.Name).Value <> "" Then
Range(IControl.Name).Font.Color = vbWhite
End If
End If
End Sub
I just learned this today and thought it might help? Most of the questions, and responses for that matter, to questions regarding checkboxes, listboxes, etc. seem to not distinguish between those forms are inserted directly on the worksheet or are imbedded in a UserForm.
This may work for a checkbox - it works for a ListBox on a UserForm. If you want code to run after a selection, the code you must write has to be in module of the UserForm. I had no clue how to access. Once you have inserted a UserForm in your VBE, add the ListBoxes or whatever to the UserForm. Right click on the UserForm in the upper left project window and select "view code." Here is where you'll place the code, in my case a "change event" such that after a selection is made from the ListBox, other code is automatically run. Her for example:
Sub lstBoxDates_Change()
Dim inputString1 As String
inputString1 = Format(UserForm1.lstBoxDates.Value, "mm-dd-yyyy")
Call EnterDates(inputString1)
Unload Me
End Sub
To explain: Again this code is in the UserForm Module. I named my ListBox,
lstBoxDates. In my main code that I call - EnterDates, I use the variable name = inputString1. The value or date that I have selected from the ListBox is captured from the UserForm1 by UserForm1.lstBoxDates.Value - and I format that to a date, otherwise you see just a number. This is for only one selection.
Because I Dim here, no need to Dim in your main code. The sub for the main code
needs to accept the variable you are passing to it:
Sub EnterDates(inputString1)
This is very generalized but maybe something will click so you can get what you are after. I hope so, for I've worked on this a full two days!!