I am trying to get rid of the horizontal scroll bar in my listbox--which appears when a user clicks in certain cells and is then consequently "deleted" each time the user clicks out of that cell (so I can't change it manually, I must change it with code)--but the .ColumnWidths property does not seem to function.
It seems the ColumnWidths is default set at 74--this based on the fact that if I set my Width at 74 or greater there is no horizontal scroll bar.
If when clicking a cell, I go into design mode, open properties, I can manually set the ColumnWidths to 35. That is not a solution since my listbox is created and deleted depending on the user's active cell. Nonetheless this confirmed that it is something about how my code is written.
Option Explicit
Private WithEvents Lbx As MSForms.ListBox
Private oTarget As Range
Private ListBoxName As String
Private Const Cell_A1 As String = "B1:B20" 'change addr as required.
Private Sub Lbx_Change()
Dim k As Long
oTarget.ClearContents
For k = 0 To Lbx.ListCount - 1
If Lbx.Selected(k) Then
If Len(oTarget) = 0 Then
oTarget = Lbx.List(k)
Else
oTarget = _
Trim(oTarget & vbNewLine & Lbx.List(k))
End If
End If
Next
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim oListBox As OLEObject
On Error Resume Next
Me.OLEObjects(1).Delete
Range(Cell_A1).Interior.ColorIndex = 0
If Target.Column = 2 And (Target.Row >= 1 And Target.Row <= 20) Then
'UCase(Target.Address(0, 0)) = UCase(Cell_A1)
Application.DisplayFormulaBar = False
Set oListBox = _
Me.OLEObjects.Add(ClassType:="Forms.ListBox.1")
With oListBox
Names.Add "ListBoxName", .Name
.Left = Target.Offset(0,1).Left
.Top = Target.Offset(0, 0).Top
.ColumnCount = 1
.ColumnWidths = "35"
.Width = 54
.Height = Me.StandardHeight * 16
.Object.ListStyle = fmListStylePlain
.ListFillRange = "A1:A20"
.Placement = xlFreeFloating
.Object.MultiSelect = fmMultiSelectMulti
.Object.SpecialEffect = fmSpecialEffectFlat
.Object.BorderStyle = fmBorderStyleSingle
With Application
.OnTime Now + _
TimeSerial(0, 0, 0.01), Me.CodeName & ".Hooklistbox"
.CommandBars.FindControl(ID:=1605).Execute
End With
End With
Else
Application.DisplayFormulaBar = True
Names("ListBoxName").Delete
Range(Cell_A1).Interior.ColorIndex = 0
End If
End Sub
Private Sub Hooklistbox()
Application.CommandBars.FindControl(ID:=1605).Reset
Set oTarget = ActiveCell
ActiveCell.Interior.Color = vbGreen
'display the listbox and hook it.
With Me.OLEObjects(Evaluate("ListBoxName"))
.Visible = True
Set Lbx = .Object
End With
End Sub
Type
.Object.
Before .ColumnCount and .ColumnWidths
And get rid of the on error resume next, which brought you to this "hidden" error in the first place
Use a on error goto 0 afterwards when it's not needed anymore
++
instead of:
On Error Resume Next
Me.OLEObjects(1).Delete
you could use:
If Me.OLEObjects.Count > 0 Then Me.OLEObjects(1).Delete
and delete this line (because Names will be overwritten, so no need to delete:
Names("ListBoxName").Delete
Related
I have an array of shapes created in a for loop and want to assign simple code to each of them as "yes/no" buttons.
The code that creates the array of buttons is as follows:
Dim i As Integer
Dim j As Integer
Dim k As Integer
For i = 1 To 3
For j = 2 To 17
ActiveSheet.Shapes.addshape(msoShapeRectangle, Cells(j, i).Left + 0, _
Cells(j, i).Top + 0, Cells(j, i).Width, Cells(j, i).Height).Select
Next j
Next i
I would like to be able to assign code to each of the shapes as they are created but do not know how. What I want the code to do for each shape looks like the below. I want the shapes to react when clicked and cycle through yes/no/blank text in each of the shapes. The general logic of the code is below
value = value +1
if value = 1, then "yes" and green
if value = 2, then "no" and red
if value = 3, then value = 0 and blank and grey
Thank you in advance for your help
You can do something like this:
Option Explicit
Sub Tester()
Dim i As Long, j As Long, k As Long
Dim addr As String, shp As Shape
For i = 1 To 3
For j = 2 To 17
With ActiveSheet.Cells(j, i)
Set shp = .Parent.Shapes.AddShape(msoShapeRectangle, .Left + 0, _
.Top + 0, .Width, .Height)
With shp.TextFrame2
.VerticalAnchor = msoAnchorMiddle
.TextRange.ParagraphFormat.Alignment = msoAlignCenter
End With
shp.Name = "Button_" & .Address(False, False)
End With
shp.Fill.ForeColor.RGB = RGB(200, 200, 200)
shp.OnAction = "ButtonClick"
Next j
Next i
End Sub
'called from a click on a shape
Sub ButtonClick()
Dim shp As Shape, capt As String, tr As TextRange2
'get a reference to the clicked-on shape
Set shp = ActiveSheet.Shapes(Application.Caller)
Set tr = shp.TextFrame2.TextRange
Select Case tr.Text 'decide based on current button text
Case "Yes"
tr.Text = ""
shp.Fill.ForeColor.RGB = RGB(200, 200, 200)
Case "No"
tr.Text = "Yes"
shp.Fill.ForeColor.RGB = vbGreen
Case ""
tr.Text = "No"
shp.Fill.ForeColor.RGB = vbRed
End Select
End Sub
Just to visualize my idea regarding using the selection change event instead of buttons:
The area that should be the clickable range is named clickArea - in this case B2:D17.
Then you put this code in the according sheet module
Option explicit
Private Const nameClickArea As String = "clickArea"
Private Enum bgValueColor
neutral = 15921906 'gray
yes = 11854022 'green
no = 11389944 'red
End Enum
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'whenever user clicks in the "clickArea" the changeValueAndColor macro is triggered
If Not Intersect(Target.Cells(1, 1), Application.Range(nameClickArea)) Is Nothing Then
changeValueAndColor Target.Cells(1, 1)
End If
End Sub
Private Sub changeValueAndColor(c As Range)
'this is to deselect the current cell so that user can select it again
Application.EnableEvents = False: Application.ScreenUpdating = False
With Application.Range(nameClickArea).Offset(50).Resize(1, 1)
.Select
End With
'this part changes the value and color according to the current value
With c
Select Case .Value
Case vbNullString
.Value = "yes"
.Interior.Color = yes
Case "yes"
.Value = "no"
.Interior.Color = no
Case "no"
.Value = vbNullString
.Interior.Color = neutral
End Select
End With
Application.EnableEvents = True: Application.ScreenUpdating = True
End Sub
And this is how it works - with each click on one of the cells value and background color are changed. You have to click on the image to start anmimation.
To reset everything I added a hyperlink that calls the reset action (and refers to itself)
Add this code to the sheets module
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
clearAll
End Sub
Private Sub clearAll()
With Application.Range(nameClickArea)
.ClearContents
.Interior.Color = neutral
End With
End Sub
I have a column in a table that will contain entire phrases with difficult words ("hypothetical exemplification of those akiophrastic words"). I have a list of most words that I expect will be used there.
I found a great solution here but it doesn't quite match my usecase. It works if you want to choose the content of your cell from a list of choices. I want to be able to get suggestions for the currently-typed word within the cell. So I write "hypoth" and click "hypothetical" from the dropdown, then I hit spacebar and start writing "exem" and want to get suggestions for that as well, and so on.
I will try changing the VBA code provided in my hyperlink above but I'm not sure I'll be successful. I'm open to any suggestion. It can also involve userforms although I doubt there is a way using them.
EDIT: On request, I'm summarizing the linked tutorial and posting its code.
It makes you create a Combo Box from the developer tools tab and name it TempCombo.
In the code for the worksheet, where the box is located, you write the following code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Update by Extendoffice: 2020/01/16
Dim xCombox As OLEObject
Dim xStr As String
Dim xWs As Worksheet
Dim xArr
Set xWs = Application.ActiveSheet
On Error Resume Next
Set xCombox = xWs.OLEObjects("TempCombo")
With xCombox
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
End With
If Target.Validation.Type = 3 Then
Target.Validation.InCellDropdown = False
Cancel = True
xStr = Target.Validation.Formula1
xStr = Right(xStr, Len(xStr) - 1)
If xStr = "" Then Exit Sub
With xCombox
.Visible = True
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width + 5
.Height = Target.Height + 5
.ListFillRange = xStr
If .ListFillRange = "" Then
xArr = Split(xStr, ",")
Me.TempCombo.List = xArr
End If
.LinkedCell = Target.Address
End With
xCombox.Activate
Me.TempCombo.DropDown
End If
End Sub
Private Sub TempCombo_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case 9
Application.ActiveCell.Offset(0, 1).Activate
Case 13
Application.ActiveCell.Offset(1, 0).Activate
End Select
End Sub
Here's a very basic example using a text box (txt1) and a listbox (lstMatches) on a worksheet:
Option Explicit
Dim suspend As Boolean
Private Sub lstMatches_Click()
Dim word, pos As Long
word = Me.lstMatches.Value
suspend = True
'try to replace the last "word" (or part of word) with the selected word
pos = InStrRev(Me.txt1.Text, " ")
If pos > 0 Then
Me.txt1.Text = Left(Me.txt1.Text, pos) & " " & word
Else
Me.txt1.Text = word
End If
Me.txt1.Activate
suspend = False
End Sub
Private Sub txt1_Change()
Dim txt As String, arr, last As String, allWords, r As Long
If suspend Then Exit Sub 'don't respond to programmatic changes
txt = Trim(Me.txt1.Text)
If Len(txt) = 0 Then 'is there any text?
Me.lstMatches.Clear
Exit Sub
End If
arr = Split(txt, " ")
last = arr(UBound(arr)) 'get the last word
If Len(last) > 1 Then
allWords = Me.Range("words").Value 'get the words list
Me.lstMatches.Clear
For r = 1 To UBound(allWords)
If allWords(r, 1) Like last & "*" Then 'match on "starts with"
Me.lstMatches.AddItem allWords(r, 1)
End If
Next r
End If
End Sub
Using the linked code from my OP and Tim Williams's excellent code, this is the result I got to. To use this, you will have to adapt some lines. There were some really odd bugs which I fixed by adapting some parts. Also added control functionality with Return (+Shift), up and down keys.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim xText As OLEObject
Dim xStr As String
Dim xList As OLEObject
Dim xWs As Worksheet
Dim xArr
Dim ListTarget As Range
' Suggestion box placement
Set ListTarget = Target.Offset(2, 1)
Set xWs = Application.ActiveSheet
On Error Resume Next
Set xText = xWs.OLEObjects("txt1")
Set xList = xWs.OLEObjects("lstMatches")
' Every click lets the boxes disappear.
With xText
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
End With
With xList
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
End With
' Restrict where you want this functionality in your sheet here
If Target.Validation.Type = 3 And Target.column = 10 And Target.row > 4 Then
Target.Validation.InCellDropdown = False
xStr = Target.Validation.Formula1
xStr = Right(xStr, Len(xStr) - 1)
If xStr = "" Then Exit Sub
With xText
.Visible = True
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width + 200 ' Size of text box
.Height = Target.Height + 5 ' Make it a little taller for better readability
.ListFillRange = ""
'If .ListFillRange = "" Then
'xArr = Split(xStr, ",")
'Me.TempCombo.list = xArr
'End If
.LinkedCell = Target.Address
End With
With xList
.Visible = True
.Left = ListTarget.Left
.Top = ListTarget.Top
.Width = ListTarget.Width + 200 ' Size of suggestions box
.Height = ListTarget.Height + 100
If .ListFillRange = "" Then 'This loop fills the suggestions with the list from the validation formula, unless already changed by input
xArr = Split(xStr, ",")
xList.ListFillRange = xArr
End If
End With
xText.Activate
Me.lstMatches.Locked = False ' It randomly locked for me, just in case.
' The following two lines fix an obscure bug that made the suggestions un-clickable at random.
ActiveWindow.SmallScroll ToLeft:=1
ActiveWindow.SmallScroll ToRight:=1
End If
End Sub
Private Sub lstMatches_Click()
Dim word, pos As Long
word = Me.lstMatches.value
suspend = True ' disables the text change function for programmatic changes
'try to replace the last "word" (or part of word) with the selected word
pos = InStrRev(Me.txt1.text, " ")
If pos > 0 Then
Me.txt1.text = Left(Me.txt1.text, pos) & word
Else
Me.txt1.text = word
End If
Me.txt1.Activate
suspend = False
End Sub
Private Sub txt1_Change()
Dim txt As String, arr, last As String, allWords, r As Long
Dim data_lastRow As Long
data_lastRow = Worksheets("my_data").Cells(2, 5).End(xlDown).row
If suspend Then Exit Sub 'don't respond to programmatic changes
txt = Trim(Me.txt1.text)
If Len(txt) = 0 Then
Me.lstMatches.Clear
Exit Sub
End If
arr = Split(txt, " ")
last = arr(UBound(arr))
If Len(last) > 1 Then
allWords = Worksheets("my_data").Range("E2:E" & CStr(data_lastRow)).value 'get the words list
Me.lstMatches.Clear
For r = 1 To UBound(allWords)
If allWords(r, 1) Like last & "*" Then 'match on "starts with"
Me.lstMatches.AddItem allWords(r, 1)
If Me.lstMatches.ListCount = 15 Then Exit Sub ' limiting it to 15 suggestions
End If
Next r
End If
End Sub
Private Sub txt1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case 13
If Shift = 0 Then
Application.ActiveCell.Offset(1, 0).Activate
Else
Application.ActiveCell.Offset(-1, 0).Activate
End If
Case vbKeyDown
Application.ActiveCell.Offset(1, 0).Activate
Case vbKeyUp
Application.ActiveCell.Offset(-1, 0).Activate
Case vbKeyLeft
Application.ActiveCell.Offset(0, -1).Activate
End Select
End Sub
I'm currently implementing some VBA code that allows a listbox to trigger on certain columns and then once filled in the cell gets filled with the selection. The initial solution has been adapted from Checkboxes for multiple values in a single cell in Excel except instead of triggering on a specific cell I want it triggered for specific cells within an entire column. I've managed to adapt this code just fine and the boxes fill in, but they only update if the next selected cell is outside that entire column (as they still fall within the intersect otherwise). Is there a way to allow intersect to account for any cell selection change? I just want the content to fill in regardless of whether I select a cell on a different column (which works) or a different row (which doesn't). I've put the code in here but it's a broad copy of the linked code above.
Thanks in advance!
Option Explicit
Dim fillRng As Range
Dim fillRngp As Range
Dim fillRngr As Range
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim LBColors As MSForms.ListBox
Dim LBPers As MSForms.ListBox
Dim LBRec As MSForms.ListBox
Dim LBobj As OLEObject
Dim LBoba As OLEObject
Dim LBObr As OLEObject
Dim i As Long
Set LBobj = Me.OLEObjects("LB_Process")
Set LBColors = LBobj.Object
Set LBoba = Me.OLEObjects("LB_Personal")
Set LBPers = LBoba.Object
Set LBObr = Me.OLEObjects("LB_Record")
Set LBRec = LBObr.Object
If Selection.Count > 1 Then
Else
If Not Intersect(Target, Range("G5:G10000")) Is Nothing Then
Set fillRng = Target
With LBColors
.Left = fillRng.Offset(0, 1).Left
.Top = fillRng.Offset(0, 1).Top
.Width = fillRng.Offset(0, 1).Width
.Visible = True
End With
Else
LBobj.Visible = False
If Not fillRng Is Nothing Then
fillRng.ClearContents
With LBColors
If .ListCount <> 0 Then
For i = 0 To .ListCount - 1
If fillRng.Value = "" Then
If .Selected(i) Then fillRng.Value = .List(i)
Else
If .Selected(i) Then fillRng.Value = _
fillRng.Value & ", " & .List(i)
End If
Next
End If
For i = 0 To .ListCount - 1
.Selected(i) = False
Next
End With
Set fillRng = Nothing
Set fillRngp = Nothing
Set fillRngr = Nothing
End If
End If
If Not Intersect(Target, Range("M5:M10000")) Is Nothing Then
Set fillRngp = Target
With LBPers
.Left = fillRngp.Offset(0, 1).Left
.Top = fillRngp.Offset(0, 1).Top
.Width = fillRngp.Offset(0, 1).Width
.Visible = True
End With
Else
LBoba.Visible = False
If Not fillRngp Is Nothing Then
fillRngp.ClearContents
With LBPers
If .ListCount <> 0 Then
For i = 0 To .ListCount - 1
If fillRngp.Value = "" Then
If .Selected(i) Then fillRngp.Value = .List(i)
Else
If .Selected(i) Then fillRngp.Value = _
fillRngp.Value & ", " & .List(i)
End If
Next
End If
For i = 0 To .ListCount - 1
.Selected(i) = False
Next
End With
Set fillRngp = Nothing
Set fillRng = Nothing
Set fillRngr = Nothing
End If
End If
If Not Intersect(Target, Range("O5:O10000")) Is Nothing Then
Set fillRngr = Target
With LBRec
.Left = fillRngr.Offset(0, 1).Left
.Top = fillRngr.Offset(0, 1).Top
.Width = fillRngr.Offset(0, 1).Width
.Visible = True
End With
Else
LBRec.Visible = False
If Not fillRngr Is Nothing Then
fillRngr.ClearContents
With LBRec
If .ListCount <> 0 Then
For i = 0 To .ListCount - 1
If fillRngr.Value = "" Then
If .Selected(i) Then fillRngr.Value = .List(i)
Else
If .Selected(i) Then fillRngr.Value = _
fillRngr.Value & ", " & .List(i)
End If
Next
End If
For i = 0 To .ListCount - 1
.Selected(i) = False
Next
End With
Set fillRng = Nothing
Set fillRngp = Nothing
Set fillRngr = Nothing
End If
End If
End If
End Sub
Try this - there's a lot of repetition in your posted version which can be factored away since all three listboxes get used the same way.
I also added in a method to synchronize the listbox with any existing data already in the cell.
Option Explicit
Dim fillRng As Range 'any previously-selected cell
Dim theOLE As OLEObject 'any visible listbox container
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim theLB As MSForms.ListBox
'any list visible ?
If Not theOLE Is Nothing Then
'clean up after previous list editing
Set theLB = theOLE.Object 'from the global
fillRng.Value = LBSelectedItems(theLB) 'comma-separated list of selections
theOLE.Visible = False
Set theOLE = Nothing 'clear globals
Set fillRng = Nothing
End If
'need to exit now?
If Target.Count <> 1 Then Exit Sub
If Target.Row < 5 Or Target.Row > 10000 Then Exit Sub
'which column are we dealing with
Select Case Target.Column
Case 7: Set theOLE = Me.OLEObjects("LB_Process")
Case 13: Set theOLE = Me.OLEObjects("LB_Personal")
Case 15: Set theOLE = Me.OLEObjects("LB_Record")
Case Else: Exit Sub '<< nothing else to do here
End Select
Set fillRng = Target ' populate globals
Set theLB = theOLE.Object
SetList fillRng, theLB ' any cell value to sync with the list?
With theLB
.Left = fillRng.Offset(0, 1).Left
.Top = fillRng.Offset(0, 1).Top
.Width = fillRng.Offset(0, 1).Width
.Visible = True
End With
End Sub
'select list items, based on any existing value in the cell
Sub SetList(rng As Range, LB As MSForms.ListBox)
Dim arr, i As Long
If Len(rng.Value) = 0 Then Exit Sub 'nothing to do...
arr = Split(rng.Value, ",") 'existing choices are comma-delimited
For i = 0 To LB.ListCount - 1
'?list item matches value from cell?
If Not IsError(Application.Match(LB.List(i), arr, 0)) Then
LB.Selected(i) = True
End If
Next i
End Sub
'return a comma-delimted list of selected items from a listbox
Function LBSelectedItems(LB As MSForms.ListBox)
Dim i As Long, lst, sep
For i = 0 To LB.ListCount - 1
If LB.Selected(i) Then
lst = lst & sep & LB.List(i)
sep = "," 'at least one selection, so need a separator
LB.Selected(i) = False 'deselect after checking
End If
Next i
LBSelectedItems = lst
End Function
I have a code here that will generate pages depends on what value is on the textbox.
'Button accepting how many number of pages
Private Sub CommandButton1_Click()
RowChar = 70
MultiPage1.Pages.Clear
For i = 0 To TextBox1.Value - 1
MultiPage1.Pages.Add
MultiPage1.Pages(i).Caption = "Variable" & i + 1
Call LabelPerPage
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "NameBox")
With txtbx
.Top = 20
.Left = 100
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "MinBox")
With txtbx
.Top = 50
.Left = 100
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "LsbBox")
With txtbx
.Top = 20
.Left = 300
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "Mataas")
With txtbx
.Top = 50
.Left = 300
End With
If i = 0 Then
FormulaString = "= C15"
Else
FormulaString = FormulaString & " " & Chr(RowChar) & "15"
RowChar = RowChar + 3
End If
Next i
TextBox2.Value = FormulaString
End Sub
Problem: I want to disable commandbutton2(button for computation of MINbox and MAxbox) if all the textboxes inside each pages are empty. Do you have any IDEA how can I do that? Thank you.
Though best way and easiest way is to validate on click in CommandButton2_Click as answered by #Excelosaurus, i just offering slightly modified way of TextBox change event trapping by #Mathieu Guindon's answer in the post Implementing a change event to check for changes to textbox values and enabling the “apply” button. The full credit of this technique of encapsulating a WithEvents MSForms control goes to #Mathieu Guindon
in the Userform1 code module may be modified as below
Public handlers As VBA.Collection ' added
Private Sub CommandButton1_Click()
RowChar = 70
MultiPage1.Pages.Clear
For i = 0 To TextBox1.Value - 1
MultiPage1.Pages.Add
MultiPage1.Pages(i).Caption = "Variable" & i + 1
'Call LabelPerPage
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "NameBox")
With txtbx
.Top = 20
.Left = 100
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "MinBox")
With txtbx
.Top = 50
.Left = 100
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "LsbBox")
With txtbx
.Top = 20
.Left = 300
End With
Set txtbx = UserForm1.MultiPage1.Pages(i).Controls.Add("Forms.TextBox.1", "Mataas")
With txtbx
.Top = 50
.Left = 300
End With
If i = 0 Then
FormulaString = "= C15"
Else
FormulaString = FormulaString & " " & Chr(RowChar) & "15"
RowChar = RowChar + 3
End If
Next i
TextBox2.Value = FormulaString
CommandButton2.Enabled = False ' added
makeEvents ' added
End Sub
Sub makeEvents() ' added
Set handlers = New VBA.Collection
Dim cnt As MSForms.Control
For i = 0 To UserForm1.MultiPage1.Pages.Count - 1
For Each cnt In UserForm1.MultiPage1.Pages(i).Controls
If TypeOf cnt Is MSForms.TextBox Then
Dim textBoxHandler As DynamicTextBox
Set textBoxHandler = New DynamicTextBox
textBoxHandler.Initialize cnt
handlers.Add textBoxHandler
'Debug.Print cnt.Name & i & "Inited"
End If
Next cnt
Next i
End Sub
Then Add a new class module to your project, call it DynamicTextBox
Option Explicit
Private WithEvents encapsulated As MSForms.TextBox
Public Sub Initialize(ByVal ctrl As MSForms.TextBox)
Set encapsulated = ctrl
End Sub
Private Sub encapsulated_Change()
Dim TextEmpty As Boolean
Dim cnt As Control
Dim i As Integer
For i = 0 To UserForm1.MultiPage1.Pages.Count - 1
For Each cnt In UserForm1.MultiPage1.Pages(i).Controls
If TypeOf cnt Is MSForms.TextBox Then
'Debug.Print cnt.Name & i & "checked"
If cnt.Value = "" Then
TextEmpty = True
Exit For
End If
End If
Next cnt
If TextEmpty = True Then
Exit For
End If
Next i
If TextEmpty Then
UserForm1.CommandButton2.Enabled = False
Else
UserForm1.CommandButton2.Enabled = True
End If
End Sub
Tried and found working
The easier way is to validate on click: in CommandButton2_Click, scan your dynamically created textboxes, and either proceed or notify the user about any validation error.
A more complicated way is to create a class that will monitor the events of a TextBox. You will create one instance of this class per TextBox you want to monitor, keeping those instances in e.g. an array. See How to add events to Controls created at runtime in Excel with VBA.
You can loop through each worksheet in your workbook, and for each worksheet - loop through all the OLEObjects. You will check the typename of the .Object, and perform your final tests there.
I would create a function that you can easily call to perform this check and return a Boolean True/False.
Function allTextboxEmpty() As Boolean
Dim oleObj As OLEObject, ws As Worksheet
allTextboxEmpty = True
For Each ws In ThisWorkbook.Worksheets
For Each oleObj In ws.OLEObjects
If TypeName(oleObj.Object) = "TextBox" Then
If oleObj.Object.Value <> vbNullString Then
allTextboxEmpty = False
Exit Function
End If
End If
Next oleObj
Next ws
End Function
If the above function returns True, then you know that all of your textboxes in the workbook are empty. You can use this function as shown in the below example:
If allTextboxEmpty Then
Worksheets("Sheet1").CommandButton2.Enabled = False
Else
Worksheets("Sheet1").CommandButton2.Enabled = True
End If
i've got dynamically generated userform consisting of labels, checkboxes and text boxes. is it possible to have a contents of a textbox selected when clicked?
this is method i'm using to create textbox:
Set NewTextBox = MainFrame.Controls.Add("Forms.TextBox.1")
With NewTextBox
.Name = "QtyTB" & row
.Value = Cells(cellrow - 1 + row, 11)
.Height = 18
.Left = 210
.Top = 18
.Width = 36
.Enabled = True
.BackColor = RGB(255, 255, 0)
End With
if i was to create textbox manually i could write on_click sub for specific text box. but as i said, code generates everything from scratch.
so if there is a property, or some other way to get it done, i would be gratefull.
Yes, this can be done by creating a class module with event handling
The following code will need a bit of adaption as there isn't much code to go on in the question...
In a class module called TextBoxEventHandler
Private WithEvents FormTextBox As MSForms.TextBox
Public Property Set TextBox(ByVal oTextBox As MSForms.TextBox)
Set FormTextBox = oTextBox
End Property
Private Sub FormTextBox_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 1 Then
With FormTextBox
.SelStart = 0
.SelLength = Len(.Text)
End With
End If
End Sub
Then in the UserForm code
Private CollectionOfEventHandlers As Collection
Private Sub UserForm_Initialize()
Dim i As Long
Dim NewTextBox As MSForms.TextBox
For i = 0 To 4
Set NewTextBox = Me.Controls.Add("Forms.TextBox.1")
With NewTextBox
.Name = "QtyTB" & i ' Row
.Value = "Text " & i ' Cells(cellrow - 1 + Row, 11)
.Height = 18
.Left = 21
.Top = 18 + i * 25
.Width = 36
.Enabled = True
.BackColor = RGB(255, 255, 0)
End With
Next i
Call InitialiseHandlers
End Sub
Private Function InitialiseHandlers()
Set CollectionOfEventHandlers = New Collection
Dim FormControl As Control
For Each FormControl In Me.Controls
If TypeName(FormControl) = "TextBox" Then
Dim EventHandler As TextboxEventHandler
Set EventHandler = New TextboxEventHandler
Set EventHandler.TextBox = FormControl
CollectionOfEventHandlers.Add EventHandler
End If
Next FormControl
End Function