I have looked through numerous posts on looping through UserForm Controls but cant seem to adjust the code i have found for my needs and need some help.
Scenario I am trying to figure out:
I have 44 text boxes on a userform whose names all start with "ch" example "chTextBox1"
When the userform activates I need to loop through all of the text boxes that start with "ch" and change the background color of those textboxes to a color based on the interior color of a cell
Below is the code that I have been messing around with and I either end up in an infinite loop or I get
Error 424
Private Sub UserForm_Activate()
Dim wb As Workbook
Dim wsRR As Worksheet
Dim bColor As Range
Dim c As Control
Dim y As String
Set wb = Application.ThisWorkbook
Set wsRR = wb.Sheets("RiskRating")
Set bColor = wsRR.Range("C3")
For Each c In JHKey.Controls
If TypeName(c) = "TextBox" Then
y = Left(c, 2)
End If
If y = "ch" Then
c.BackColor = bColor.Interior.Color
End If
Next c
End Sub
Try placing the If statement testing for "ch" within the If statement testing for "TextBox". Also, you should specify the Name property for the control when checking for its name, otherwise it defaults to its Value property. Also, as an aside, I would suggest replacing JHKey with the keyword Me, which refers to the userform itself regardless of its name.
Private Sub UserForm_Activate()
Dim wb As Workbook
Dim wsRR As Worksheet
Dim bColor As Range
Dim c As Control
Dim y As String
Set wb = Application.ThisWorkbook
Set wsRR = wb.Sheets("RiskRating")
Set bColor = wsRR.Range("C3")
For Each c In Me.Controls
If TypeName(c) = "TextBox" Then
y = Left(c.Name, 2)
If y = "ch" Then
c.BackColor = bColor.Interior.Color
End If
End If
Next c
End Sub
Related
I need to list the captions of a large number of old ActiveX command buttons on a worksheet.
How do I refer to the collection?
PseudoCode:
For each btn in Activesheet.CommandButtons
Debug.Print btn.Caption
Next btn
Dim s As Worksheet
Set s = ActiveSheet
Dim o As OLEObject
For Each o In s.OLEObjects
If TypeName(o.Object) = "CommandButton" Then
Debug.Print o.Object.Caption
End If
Next
Updated the code that i found from MrExcel website:
Dim BtnActX As Integer
Dim MyShapes As OLEObjects
Dim Btn As OLEObject
'OLE Programmatic Identifiers for Commandbuttons = Forms.CommandButton.1
Set MyShapes = ActiveSheet.OLEObjects
For Each Btn In MyShapes
If Btn.progID = "Forms.CommandButton.1" Then
BtnActX = BtnActX + 1
abc = Btn.Object.Caption
MsgBox "command button text is: " & abc
End If
Next
How about looping through the OLEObjects in your worksheet, you can use their progID and see it it equals "Forms.CommandButton.1".
Note: please don't use ActiveSheet, instead use fully qualified objects, like Worksheets("Sheet1").
Option Explicit
Sub FindCommandButtonsInOLEObjects()
Dim Sht As Worksheet
Dim Obj As OLEObject
Set Sht = ThisWorkbook.Sheets("Sheet1") '<-- modify "Sheet1" to your sheet's name
' loop thourgh all OLE objects in "Sheet1"
For Each Obj In Sht.OLEObjects
If Obj.progID Like "Forms.CommandButton.1" Then ' <-- check if current object is type Comman Button
Debug.Print Obj.Object.Caption
End If
Next Obj
End Sub
I have made a userform. It contains around about 19 combo boxes. Combo boxes have 2 options YES and NO. then comes a text box infront of each combo box, where comments are typed. What I want is that if user selects no from combo box I want to copy paste the comments of that combo box from userform onto another excel sheet. Right now I am copy pasting all comments. So I want to add this feature as well. Below is the code I am currently using. Can anybody help me in upgrading this code, to add above mentioned feature as well.
Private Sub ()
Dim ws As Worksheet
Set ws = Worksheets("PQCILDMS")
Dim newRow2 As Long
newRow2 = Application.WorksheetFunction.CountA(ws.Range("A:A")) + 1
ws.Cells(newRow2, 1).Value = cmbDMS.Value
Dim newRow3 As Long
newRow3 = Application.WorksheetFunction.CountA(ws.Range("A:A")) + 1
ws.Cells(newRow3, 1).Value = cmbYesNo.Value
Dim newRow4 As Long
newRow4 = Application.WorksheetFunction.CountA(ws.Range("A:A")) + 1
ws.Cells(newRow4, 1).Value = Me.txtComments.Value
ws.Cells(newRow4, 1).Columns.AutoFit
End Sub
I want to copy paste the comments of that combo box from userform
I think you mean copy TextBox comments?
The best way to handle this is name your ComboBoxes as ComboBox1, ComboBox2..ComboBox19. Similarly for the TextBoxes, name them as TextBox1, textBox2... TextBox19. Ensure that TextBox1 is in front of ComboBox1 and so on.
The reason we do this is so that it become easier to loop. See this example
Private Sub CommandButton1_Click()
Dim ws As Worksheet
Dim lRow As Long, i As Long
'~~> Change this to the relevant sheet
Set ws = Sheet1
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
For i = 1 To 19
If Me.Controls("ComboBox" & i).Value = "No" Then
.Cells(lRow, 1).Value = Me.Controls("TextBox" & i).Value
lRow = lRow + 1
End If
Next i
End With
End Sub
as an alternative to appropriately renaming texboxes and comboboxes facing each other (suggested approach), you could get the textbox facing a given combobox by checking whether textbox horizontal axis (e.g.: its medium ordinate in the Userfom layout) crosses the combobox
so you could put the following code into your userfom code pane:
Option Explicit
Dim Cbs As Collection '<--| set this collection as Userform scoped variable
Dim Tbs As Collection '<--| set this collection as Userform scoped variable
Private Sub CommandButton1_Click()
Dim cb As MSForms.ComboBox, tb As MSForms.TextBox
Dim el As Variant
With Worksheets("PQCILDMS") '<--| reference sheet
For Each el In Cbs '<--|loop through all userform comboboxes
Set cb = el '<--|set the current combobox control
If cb.value = "NO" Then '<--|if its value is "NO" ...
Set tb = GetTbNextToCb(cb, Tbs) '<--|... look for the textbox whose horizontal axis is inbetween the current combobox
If Not tb Is Nothing Then .Cells(.Rows.Count, 1).End(xlUp).Offset(1).value = tb.value '<--|... if found it then write its content in referenced sheet column "A" next available cell
End If
Next el
End With
End Sub
Function GetTbNextToCb(cb As MSForms.ComboBox, Tbs As Collection) As MSForms.TextBox
Dim tb As MSForms.TextBox
Dim cbYMin As Long, cbYMax As Long, tbYMin As Long, tbYMax As Long
Dim el As Variant
GetYMinMax cb, cbYMin, cbYMax '<--| get minimum and maximum ordinate of passed combobox
For Each el In Tbs '<--|loop through all userform textboxes
Set tb = el '<--|set the current textbox control
If IsAxisInBetween(tb, cbYMin, cbYMax) Then '<--|if current textbox horizontal axis inbetween passed combobox minimum and maximum ordinates...
Set GetTbNextToCb = tb '...return the found textbox...
Exit Function '<--|... and exit function (no need to iterate over remaining textboxes)
End If
Next el
End Function
Function IsAxisInBetween(ctrl As Control, yMinRef As Long, yMaxRef As Long) As Boolean
Dim yMin As Long, yMax As Long
GetYMinMax ctrl, yMin, yMax '<--| get minimum and maximum ordinates of the control in the userform
IsAxisInBetween = (yMax + yMin) / 2 <= yMaxRef And (yMax + yMin) / 2 >= yMinRef '<--| check if the control orizontal axis is in between the reference ordinates
End Function
Sub GetYMinMax(ctrl As Control, yMin As Long, yMax As Long)
With ctrl
yMin = .Top '<--| get the minimum ordinate of the control in the Userform
yMax = .Top + .Height '<--| get the maximum ordinate of the control in the Userform
End With
End Sub
'this sub will run at Userfom loading
Private Sub UserForm_Initialize()
Set Cbs = GetCtrls("ComboBox") '<--| gather all Userform comboboxes in this collection
Set Tbs = GetCtrls("TextBox") '<--| gather all Userform texboxes in this collection
End Sub
Function GetCtrls(ctrlTypeName As String) As Collection
Dim coll As New Collection '<--| declare and set a new Collection object
Dim ctrl As Control
For Each ctrl In Me.Controls '<--| loop through all Userform controls
If TypeName(ctrl) = ctrlTypeName Then '<--| if it matches the passed Type name...
coll.Add ctrl, ctrl.Name '<--| ... then add it to the collection
End If
Next ctrl
Set GetCtrls = coll '<--| return the collection
End Function
I am trying to add a button to an Excel workbook so that it shows up in every sheet. A great answer to my original question gave me a macro to create the buttons on each sheet:
Sub AddButtons()
Dim ws As Excel.Worksheet
Dim btn As Button
For Each ws In ThisWorkbook.Worksheets
Set btn = ws.Buttons.Add(X, Y, W, H)
[set btn properties]
Next ws
End Sub
I am now having trouble with setting the button properties so that the button prints the sheet when pressed. Again here is my print macro:
Dim WS_Count As Integer
Dim i As Integer
' Set WS_Count equal to the number of worksheets in the active workbook.
WS_Count = ActiveWorkbook.Worksheets.Count
'allows user to set printer they want to use
Application.Dialogs(xlDialogPrinterSetup).Show
' Begin the loop.
For i = 5 To WS_Count
Worksheets(i).Activate
With ActiveWorkbook.Worksheets(i).PageSetup
.PrintArea = "A1:O48"
.Orientation = xlLandscape
.Zoom = False
.FitToPagesTall = 1
.FitToPagesWide = 1
End With
ActiveWorkbook.Worksheets(i).PrintOut
There have been some good suggestions about how to go about incorporating this macro into the button properties (passing variables and creating a new print sub) however I am pretty new to VBA and have been unsuccessful in getting this to work. Ideally I would have a button macro that creates the button and every time it is pressed calls the print macro for each sheet.
One last thing, I am trying to change the button code so that it only adds buttons to sheet 5 onwards. It would be great if anyone knew how to do that as well?
Any advice is helpful and greatly appreciated!
Try this:
Sub AddButtons()
Dim ws As Excel.Worksheet
Dim btn As Button
For Each ws In ThisWorkbook.Worksheets
Set btn = ws.Buttons.Add(X, Y, W, H)
btn.OnAction = "MySub" ' MySub is executed when btn is clicked
' Substitute the name of your printing subroutine
btn.Caption = "Print"
'set additional btn properties as needed
Next ws
End Sub
X and Y determine the location, W and H determine the button size.
This will add a button (Form Control) and assign an existing macro to it.
Sub test()
Dim cb As Shape
Set cb = Sheet1.Shapes.AddFormControl(xlButtonControl, 10, 10, 100, 25)
cb.OnAction = "PrintMacro"
End Sub
Private Sub PrintMacro()
MsgBox "Test" ' for testing pursposes
' you actually put your print code here
End Sub
Now to add buttons from Sheet 5 onwards only, you can try:
Producing a list of all your sheet names (if there's only a few of them)
Dim shname
For Each shname In Array("Sheet 5", "Sheet 6", "Sheet 7")
test Sheets(shname) ' note that you'll have to use below test sub
Next
Do it the other way around. Make a list of what to exclude and test every sheet if it is on the list or not.
Dim sh As Worksheet
Dim xcludesheet: xcludesheet = Array("Sheet1", "Sheet2", "Sheet3", "Sheet4")
For Each sh In Worksheets
If IsError(Application.Match(sh.Name, xcludesheet, 0)) Then
test Sheets(sh.Name)
End If
Next
Your test sub to be used in above samples.
Sub test(ws As Worksheet)
Dim cb As Shape
Set cb = ws.Shapes.AddFormControl(xlButtonControl, 10, 10, 100, 25)
cb.OnAction = "PrintMacro"
End Sub
I'm trying to get a single macro that I can assign to my command buttons. I have multiple buttons that open different files so in each cell I include a different file path.
Currently my command buttons are looking for a specific cell reference and opening that value.
Is there any way I can get the macro to look for the value in the cell to which it is aligned?
I'm using two macros at the moment - one to create the buttons and then another to assign to the buttons. I am having to create a new macro for each button.
Macro to create button...
Sub Buttons()
Dim i As Long
Dim lRow2 As Integer
Dim shp As Object
Dim dblLeft As Double
Dim dblTop As Double
Dim dblWidth As Double
Dim dblHeight As Double
With Sheets("Print Schedule")
dblLeft = .Columns("A:A").Left 'All buttons have same Left position
dblWidth = .Columns("A:A").Width 'All buttons have same Width
For i = Range("E65536").End(xlUp).Offset(1, 0) To ActiveCell + 15
dblHeight = .Rows(i).Height 'Set Height to height of row
dblTop = .Rows(i).Top 'Set Top top of row
Set shp = .Buttons.Add(dblLeft, dblTop, dblWidth, dblHeight)
shp.Characters.Text = "Open Print Schedule"
Next i
End With
End Sub
Macros to open file...
Sub Mendip()
Dim myfile As String
myfile = Cells(6, 6).Value
Application.Workbooks.Open Filename:=myfile
End Sub
Please tell me there is a better way to do this!
When you create the form buttons as shown below then you can assign a common macro to them
And you can assign a macro like this
Sub Sample()
Dim shp As Shape
Set shp = ActiveSheet.Shapes(Application.Caller)
'MsgBox shp.TopLeftCell.Address
Select Case shp.TopLeftCell.Address
Case "$A$1"
'~~> Do Something
Case "$B$1"
'~~> Do Something
'
'~~> And So on
'
End Select
End Sub
EDIT:
One thing I forgot to mention. To assign the "Sample" macro to all buttons, Add the below line after you create the shape.
shp.OnAction = "Sample"
I currently run a macro to compare the most recent sheet of data to the report immediately prior and highlight changes. It works fine on its own. Now, however, we would like to be able to compare selected sheets from any time period. My idea was to pop up a simple userform with two textboxes that the user can use to specify which two reports he wants to compare. I am quite lost though with the idea of trying to declare public variables; what I've got atm is:
Option Explicit
Public shtNew As String, shtOld As String, _
TextBox1 As TextBox, TextBox2 As TextBox
Sub SComparison()
Const ID_COL As Integer = 31 'ID is in this column
Const NUM_COLS As Integer = 31 'how many columns are being compared?
Dim rwNew As Range, rwOld As Range, f As Range
Dim X As Integer, Id
shtNew = CSManager.TextBox1
shtOld = CSManager.TextBox2
'Row location of the first employee on "CurrentMaster" sheet
Set rwNew = shtNew.Rows(5)
Do While rwNew.Cells(ID_COL).Value <> ""
Id = rwNew.Cells(ID_COL).Value
Set f = shtOld.UsedRange.Columns(ID_COL).Find(Id, , xlValues, xlWhole)
If Not f Is Nothing Then
Set rwOld = f.EntireRow
For X = 1 To NUM_COLS
If rwNew.Cells(X).Value <> rwOld.Cells(X).Value Then
rwNew.Cells(X).Interior.Color = vbYellow
rwNew.Cells(33) = "UPDATE"
Else
rwNew.Cells(X).Interior.ColorIndex = xlNone
End If
Next X
End If
Set rwNew = rwNew.Offset(1, 0) 'next row to compare
Loop
Call SUpdates
End Sub
My Suggestion would be to use Comboboxes instead of TextBoxes. Create a userform with two command buttons and two comboboxes and populate the comboboxes in the UserForm_Initialize() event using this code.
Private Sub UserForm_Initialize()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
ComboBox1.AddItem ws.Name: ComboBox2.AddItem ws.Name
Next
End Sub
And then use this code in the OK button to do the comparison.
Private Sub CommandButton1_Click()
Dim shtNew As Worksheet, shtOld As Worksheet
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select the first sheet"
Exit Sub
End If
If ComboBox2.ListIndex = -1 Then
MsgBox "Please select the Second sheet"
Exit Sub
End If
Set shtNew = Sheets(ComboBox1.Value)
Set shtOld = Sheets(ComboBox2.Value)
'~~> REST OF THE CODE HERE NOW TO WORK WITH THE ABOVE SHEETS
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub
HTH
Sid
For an easy fix, couldn't you just colour (sorry, I'm English!) the worksheets that you want to refer to, then do something like:
Sub ListSheets()
'lists only non-coloured sheets in immediate window
'(could amend to add to combo boxes)
Dim w As Worksheet
'loop over worksheets in active workbook
For Each w In Worksheets
If w.Tab.Color Then
'if tab color is set, print
Debug.Print w.Name
End If
Next w
Let me know if this solves your problem.