Merging Text Into Specific Place in Textbox from Combobox Using .SelStart & .SelLength - vba

As the title suggests, I'm trying to merge prefilled text values from a combobox into a specific place in a textbox on my form. **NOTE** Neither the form nor the textbox are bound to any data source.
I have a textbox (Me.EmailMessage) with text data. I'd like to be able to save my cursor place in the textbox, then click a combobox (cboFields) to select a field from a table, which is stored at cboFields.Column(1). After selecting the field, I'd like the textbox to merge the value of the field selected into the same place where my cursor was when the textbox loss focus to move to the dropdown box.
The logic goes like this:
Before Me.EmailMessage loses focus, the cursor position within Me.EmailMessage will be saved to a variable called vCursorPosition and the text value of Me.EmailMessage will be saved to a variable called vOriginalText.
After the user selects the text value from cboFields, the value of cboFields.Column(1) will be saved to a variable called vMergeText.
The focus will be set to Me.EmailMessage, and the value of Me.EmailMessage will be replaced with the value of vOriginalText.
The value of vMergeText will be inserted at the location stored in vCursorPosition.
Here's the code I'm using:
Dim vCursorPosition As Long
Dim vOriginalText As String
Dim vSelectionLength As Long
Dim vMergeText As String
Private Sub EmailMessage_AfterUpdate()
vCursorPosition = Me.EmailMessage.SelStart
vSelectionLength = Me.EmailMessage.SelLength
vOriginalText = Me.EmailMessage.Text
End Sub
Private Sub cboFields_AfterUpdate()
vMergeText = Me.cboFields.Column(1)
Me.EmailMessage.SetFocus
Me.EmailMessage.Text = vOriginalText ''ERROR HAPPENS HERE''
Me.EmailMessage.SelStart = vCursorPosition
Me.EmailMessage.SelLength = vSelectionLength
Me.EmailMessage.SelText = vMergeText
End Sub
I'm getting this error when I run the above code:
Run-time error '2115': The macro or function set to the BeforeUpdate or ValudationRule property for this field is preventing Microsoft Access from saving the data in the field.
However, there is no code or macro set to the BeforeUpdate or ValidationRule property for Me.EmailMessage.
You can see where the code stalls out, above, where after the user has selected the text from cboFields.Column(1), the focus moves to Me.EmailMessage then attempts to set the value of Me.EmailMessage to the stored string value of vOriginalText.
Any ideas?

SOLVED
I was getting an error because I can't use the .Text property unless the control I was using it with has the focus. I tried this instead and it worked beautifully:
Dim vCursorPosition As Long
Dim vOriginalText As String
Dim vSelectionLength As Long
Dim vMergeText As String
Private Sub cboFields_AfterUpdate()
vMergeText = Me.cboFields.Column(1)
Me.EmailMessage = Left(vOriginalText, vCursorPosition - 1) & vMergeText & Mid(vOriginalText, vCursorPosition + vSelectionLength)
End Sub
Private Sub EmailMessage_AfterUpdate()
vOriginalText = Me.EmailMessage
vCursorPosition = Me.EmailMessage.SelStart
vSelectionLength = Me.EmailMessage.SelLength
If Me.Dirty Then
DoCmd.RunCommand (acCmdSaveRecord)
End If
End Sub

Related

Checking and Unchecking Checkboxes in Access

I have a form in MS Access with multiple checkboxes which I want to use to fill up one textbox. If one of the checkboxes gets unchecked, I want its value to be deleted from the textbox without deleting other values. I'm new at using Access and coding in VBA (been reading ebooks for the past 3 weeks) and although I've tried to do research online it's been difficult for me to find the right code.
This is what I have so far:
First code found
Private Sub cb_click()
If Me.cb1 = True Then
Me.txtComentarios.Value = "INACTIVO;"
Else
Me.txtComentarios.Value = Null
End If
End Sub
Second code found
Private Sub cb2_Click()
If Me.cb2 = -1 Then
Me.[txtComentarios] = [txtComentarios] & "DISCREPANCIA"
Else
Me.[txtComentarios] = ""
End If
Exit Sub
End Sub
Also I would like for the checkboxes to fill the textbox in the same order the chechboxes are displayed.
Ex.
cb1; cb2; cb3
If, cb2 gets unchecked and its value gets deleted, I should have "cb1; cb3" but if I re-check cb2 I should get "cb1; cb2; cb3" again.
I just hope someone could guide me in. Thank you in advance.
Luz
You don't need events for each checkbox. Just create one procedure, which creates full text depending on checkboxes state and puts this text to the textbox. To call this function after each click on checkbox set After Update property of all checkboxes to =MyFunctionToUpdateTextbox instead of [Event Procedure]
Private Function MyFunctionToUpdateTextbox()
Dim strText As String
If Me.cb1 = True Then
strText = strText & "INACTIVO;"
End If
If Me.cb2 = True Then
strText = strText & "DISCREPANCIA;"
End If
If Me.cb3 = True Then
strText = strText & "Text for cb3"
End If
Me.txtComentarios = strText
End Function

Question on multiple checkboxes launching code

I have a user form and a frame with 35 checkboxes in it, numbered 1 to 35. They represent 35 Named Ranges. I test to see if any of the name ranges are not set, if set correctly the checkbox value is set to TRUE.
I found some code that would allow me to trigger a sub if one of the checkboxes is clicked. That code seems to work, but my check code above also triggers the checkbox events, which I do not want. I only want the sub to run when the checkbox is clicked with the mouse? I can post the code I'm using, but though I'd first ask the question to see if what I would like to do is possible.
Thanks,
Jim
Code in class module:
Public WithEvents ChkBox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set ChkBox = ctrl
End Sub
Public Sub chkBox_Click()
If chkBoxProcess = "Y" Then
'ThisWorkbook.Worksheets("Sheet1").Range(ChkBox.Name).Value = Format(Now, "dd.mm.yyyy")
'MsgBox ("check box number = " & ChkBox.Name & " " & ChkBox.Value)
' Else
End If
End Sub
Code in Forms:
Public Sub UserForm_Initialize()
Dim SheetCount, i As Integer
Dim sh As Worksheet
'Public SheetName, SheetName2, StartOldNewTimeA, OldNewTimeAdd As String
'Initialize the form frmChgNameRng
'Set array values of the day options
'Set array values for 12:00 timeframes
'Set array values for 12:30 timeframes
'Set colors used in Checkboxes
'Set array for Checkboxes (boxes are numbered accross the page, 1 corressponds to Mon_1200/Mon_1230, 8 corresponds to Mon_200/Mon_230, etc.)
'Formulas are placed in the time cells on the left of the page, the macro will add the appropriate value into the Mon_1200 time slot and all other cells update off that cell
chkBoxProcess = "N"
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
'..... lots of code for Range Name Checks, etc.
End Sub
Your code is conflating control state with model data, and so the only way to tell it "named range 32 is ON", or "named range 13 is OFF", is to alter a checkbox' state, which fires that control's Change event.
There's no way around that, it's just how controls work: they fire a Change event whenever their value changes, regardless of how that's done.
Instead of having controls' state be the data, make the controls' state alter the data.
This requires conceptualizing this data, first: looks like you need to associate a number/index to some Boolean value. An array can do this.
Private namedRangeStates(1 To 35) As Boolean
Note that depending on what you're doing, initializing the state should be feasible by iterating the workbook's Names collection in the UserForm_Initialize handler. Or better, the form could expose a method that takes an array of Boolean values, and copies that state into namedRangeStates.
Now, when a checkbox is modified, make it alter the state:
Private Sub Checkbox31_Change()
namedRangeStates(31) = Checkbox31.Value
End Sub
Your form can expose that state as a property:
Public Property Get NamedRangeState(ByVal index As Long) As Boolean
NamedRangeState = namedRangeStates(index)
End Property
Public Property Let NamedRangeState(ByVal index As Long, ByVal value As Boolean)
namedRangeStates(index) = value
End Property
And now you can modify the enapsulated state independently of the combobox values.

How can I use the InStr function to search and highlight a value in a multiline textbox?

I am attempting to create a search inside of a multiline textbox that will find each instance of a value and highlight that value inside the textbox. It will need to work regardless of how many instances exist on a given line. So far I have used the following code to identify if a value exists on a line but cannot get the highlighting to work properly when there are multiple instances of the same value.
strVal = "Find Me"
arrLines =Split(myTextbox.value, vbCrLf)
For Each strLine In arrLines
If InStr(strVal, myTextbox.text) > 0 Then
myTextbox.SelStart = InStr(strVal, my textbox.value)
myTextbox.SelLength = Len(strVal)
Exit For
End if
Next
I want to have this macro linked to a button and have the macro find and highlight the next instance each time the button is clicked regardless if that instance is on the same line or a new line. Basically, a Ctrl+F feature for the textbox. Thanks!
You can give this code a try (see comments for explanations):
Option Explicit
Private Sub CommandButton1_Click()
Static lastInstancePosition As Long ' use Static variable to have its value persist after Sub exiting
Dim instancePosition As Long
strVal = "Find me"
With myTextbox
instancePosition = InStr(lastInstancePosition + 1, .Text, strVal) 'search string occurrence starting from last found item
If instancePosition > 0 Then
.SetFocus 'bring focus back ti the textbox after it has been taken by the button
.SelStart = instancePosition - 1
.SelLength = Len(strVal)
lastInstancePosition = instancePosition ' update starting position for next search
End If
End With
End Sub

Excel Checkbox DoubleClick event

I have a spreadsheet, basically a to-do list that has checkpoints marked by checkboxes. Rules for the checkboxes are:
1) Only the first checkbox (named checkbox_1) can be checked first.
2) Checkboxes can only be checked sequentially (i.e. checkbox_1, checkbox_2, checkbox_3 etc.)
Initially all checkboxes except for checkbox_1 are disabled. When an enabled checkbox is clicked, the next checkbox becomes enabled and disables the previous one.
The trouble I am having now is that double-clicking bypasses the enabled status of the checkbox and will go ahead and check the checkbox via the event handler.
How can I prevent this? Do I need to write something in the _BeforeDoubleClick event handler?
My event handler for the checkbox_click event is here:
Public Sub CheckBoxClick(Optional checkboxName As String = "")
'This calls the checkbox function to create the timestamp and enable the next visible checkbox
Dim assocTime As Range
Dim callerName As String
Dim aCaller As Variant
Dim check_num As String
Dim aCheckboxes As Variant
Set aCheckboxes = common.GetSheet(1).CheckBoxes
If checkboxName <> "" Then
callerName = checkboxName
Else
callerName = Application.Caller
End If
aCaller = Split(callerName, "_")
check_num = aCaller(1)
Set assocTime = common.GetRange("time_" & check_num)
If assocTime.value = "" Then
Call obj.ClickCheckBox(callerName, Constants.mypw)
End If
If check_num = aCheckboxes.count Then
If MainSheetFunctions.HasRedCells Then
common.MessageBox ("This batch record has cells with errors. Please address all red cells before continuing.")
Exit Sub
End If
End If
End Sub
The obj.ClickCheckBox sub will write the current time in an adjacent cell, and enable the next checkbox.
Thank you for your time,

Manipulating ListBoxes as Objects

I am working on a VBA userform that includes ListBoxes.
So far, when I had to manipulate one or more, I always proceeded like this in my subs, with dlg as the dialogbox name, and it did not pose any problem, given that I never wanted to do anything complicated:
Dim List1 As Object
...
List1 = dlg.GetControl("CBXname")
...
List1.addItem("String",index)
...
Now I would like to do the following in this Sub
...
If (List1.Exists(Cell1.String) = False) Then
List1.addItem(Cell1.String,k)
End If
...
List1.Clear
...
But I can do neither since List1 is an Object. However, if I decide to declare List1 as a Listbox instead, I do not know how to get the proper control on the ListBox from the dialogbox (the current getcontrol gives me an error).
One of the issues with your code is that listbox objects do not have an "exists" property. To check if a value already exists in your listbox items, you will need to loop through the items.
dim i as integer
for i = 0 to List1.listcount - 1
if List1.column(0, i) = myvalue then
'myvalue exists in List1, skip
else
List1.additem myvalue
end if
next i
Where myvalue is whatever value you are trying to add to the listbox. But that brings us to the second issue in your code which is where you add "Cell1.String". If you are trying to add a value from a worksheet range you will need to refer to that range's value, as worksheet ranges do not have a "string" property as you use it here. Ie. Cell1 = Range("A1").value
As for getting control of the listbox, you can simply refer to the objects name as an object of the form. For example, dlg.List1, if the object's name is List1.
Here is a general purpose routine you can call for any list box. The calling code assumes a list box called ListBox1, a text box called TextBox1, and a Command Button called CommandButton. When you click on the button, it searches the listbox for the text from textbox1.
Private Function ExistsInListbox(ByRef aListBox As msforms.ListBox, ByVal Item As String) As Boolean
Dim booFound As Boolean
booFound = False
Dim t As Integer
ExistsInListbox = False
For t = 0 To aListBox.ListCount - 1 'correction, aListBox not ListBox1
If Item = aListBox.List(t) Then
'if we find a match, short-circuit the loop
booFound = True
Exit For
End If
Next
ExistsInListbox = booFound
End Function
private sub CommandButton_click()
Dim answer As String
Dim val As Boolean
val = ExistsInListbox(Me.ListBox1, TextBox1.Text)
If val Then
answer = "found"
Else
answer = "Not Found"
End If
MsgBox "found-" & answer
End Sub