Excel - VBA - Compile error: Method or data member not found - excel

I create a form dynamically and fill it with check boxes generated based on all column names of the Excel sheet it is launched from.
I add also a command button.
Here is the code put directly on the form:
Option Explicit
Dim cmdArray() As New Class1
Private Sub UserForm_Initialize()
Dim lastCol As Integer
Dim i As Integer
Dim chkBox As MSForms.CheckBox
Dim myButton As Control
lastCol = Worksheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
Me.Height = 500
Me.Width = 600
For i = 1 To lastCol
Set chkBox = Me.Controls.Add("Forms.CheckBox.1", i)
chkBox.Caption = Worksheets(1).Cells(1, i).Value
' chkBox.Name = i
If i Mod 2 = 1 Then
chkBox.Left = 5
chkBox.Top = 5 + (i - 1) * 10
chkBox.Width = 200
Else
chkBox.Left = 250
chkBox.Top = 5 + (i - 2) * 10
chkBox.Width = 200
End If
Next i
i = 1
Set myButton = Me.Controls.Add("Forms.CommandButton.1", "MyButton", False)
With myButton
.Left = 500
.Top = chkBox.Top - 50
.Width = 50
.Caption = "Hide"
.Visible = True
End With
ReDim Preserve cmdArray(1 To i)
Set cmdArray(i).CmdEvents = myButton
Set myButton = Nothing
Me.ScrollBars = fmScrollBarsVertical
Me.ScrollHeight = chkBox.Top + 20
End Sub
The form is generated without issue: all check-boxes and the command button are set correctly.
I am then supposed to select which columns I want to hide from my Excel sheet, and therefore I tick the relevant checkbox. So far so good. Here is the code set is a Class :
Option Explicit
Public WithEvents CmdEvents As MSForms.CommandButton
Private Sub CmdEvents_Click()
Dim i As Integer
Dim cbx As MSForms.Control
Dim colNum As Integer
i = 0
For Each cbx In Me.UserForm1.Controls
If TypeName(cbx) = "CheckBox" Then
If cbx.Value = True Then
colNum = cbx.Name - i
Worksheets(1).Columns(colNum).EntireColumn.Delete
i = i + 1
End If
End If
Next ctrl
End Sub
When I click the button, it is supposed to trigger the hiding of the columns in the Excel sheet, however, I got the following error:
Compile error: Method or data member not found
This error is reported in the code in the Class module, highlighting the term .UserForm1 and if I remove this .UserForm1, then still the same error highlighting the .Controls.
I am not a great specialist of VBA, I manage usually to create simple codes and reusing samples I can find here and there, but this time, I run out of idea (and understanding), so thanks in advance for any help.

Related

Reading value from dynamic textbox

I've an excel vba form named as 'UserForm'. Overall, there's a button (name as 'buttonAdd') that the function could create/add textbox based on inputed (from 'a' variabel) value dynamically. Here's the code.
Public Sub buttonAdd_Click()
Dim a As Integer
a = TextBox1.Value
Dim cCntrl As Control
For i = 1 To a
Set cCntrl = Me.Controls.Add("Forms.TextBox.1", "quarter", True)
With cCntrl
.Name = "quarter" & d
.Width = 150
.Height = 25
.Top = 100 + (d * 25)
.Left = 220
.ZOrder (0)
End With
Next i
Now, I've a problem. I can't take/get the value from those dynamic textbox. I tried using this code for saving the value into 'A1' cell, but it's doesnt work.
Private Sub SaveButton_Click()
Dim a As Integer
Dim Ws As Worksheet
Dim cCntrl As Control
Set Ws = Worksheets("Sheet1")
Ws.Range("A1").Value = quarter1.Value
End Sub
I thought the name of first dynamic textbox is quarter1, but when I tried to get the value, it didn't work. Is there anyway or hint for solving this issue. Just saying: just take the value of the first text box, write it there, then take another one and so on.
Thanks a lot.
Looks like your problem is a simple typo. You have used d instead of i (d is undefined in your code). I recommend always using Option Explicit.
Public Sub buttonAdd_Click()
Dim a As Long ' Minor, but Integer I sjust too short these days for a lot of things.
Dim i as Long
a = TextBox1.Value
Dim cCntrl As Control
For i = 1 To a
Set cCntrl = Me.Controls.Add("Forms.TextBox.1", "quarter", True)
With cCntrl
.Name = "quarter" & CStr(i) ' explicit rather than implicit conversion
.Width = 150
.Height = 25
.Top = 100 + (i * 25)
.Left = 220
.ZOrder (0)
End With
Next I
Edit: Might be a couple more typos in the .Add command (e.g. you might have wanted "quarter" & "1" instead of just "quarter"?) but I don't have the VBA reference up at the moment to check.

Re-write dynamic textbox after button is pressed

I've created a code in VBA to collect data using a multi-page control. In each page, I've added checkboxes dynamically based on rows from the worksheet in Excel and, for each checkbox, there's a textbox and 2 command buttons, just like the image below:
Input Window:
The code to automatically add controls is:
Private Sub UserForm_Initialize()
fmat_disp.Value = 0
fmat_set.Value = 0
'---------------------------------------------------------------------------------------------
'Inspeção de Mecânica
Sheets("Mecânica").Activate
n_anom = Application.WorksheetFunction.CountA(Range("1:1")) - 1
AreasInspecao.mecanica.ScrollHeight = 10 + 18 * (n_anom)
For i = 1 To n_anom
'Selecionar anomalia
Set SelAnom = AreasInspecao.mecanica.Controls.Add("Forms.CheckBox.1", "sel_anom_" & i)
SelAnom.Caption = Worksheets("Mecânica").Cells(1, i + 1)
SelAnom.AutoSize = True
SelAnom.Height = 18
SelAnom.Left = 5
SelAnom.Top = 5 + (SelAnom.Height) * (i - 1)
SelAnom.Tag = i
Same goes to the textbox and plus/minus buttons, only changing the captions.
What I want is:
1) when CHECKBOX is CHECKED, respective TEXTBOX must show 1
2) when MINUS sign is PRESSED, respective TEXTBOX must decrement
3) when PLUS sign is PRESSED, respective TEXTBOX must increment
4) when "Finalizar Inspeção" is PRESSED, all data collected must be sent to Excel, filling a worksheet.
I simply don't know how to link each button/checkbox to your respective textbox without creating a subroutine for each one! I'll have ~500 subroutines....that's impossible to manage manually....
OK here's a rough outline for handling the click events on the checkboxes and buttons.
First two custom classes for capturing the clicks: each of these is very simple - all they do is call a method on the userform with the clicked control as an argument.
'clsCheck
Public WithEvents chk As MSForms.CheckBox
Private Sub chk_Click()
frmExample.HandleClick chk
End Sub
'clsButton
Public WithEvents btn As MSForms.CommandButton
Private Sub btn_Click()
frmExample.HandleClick btn
End Sub
Userform code - my form is named "frmExample".
Note the naming convention which allows groups of controls to be treated as a "unit".
Option Explicit
'These two global collections hold instances of the custom classes
Dim colCheckBoxes As Collection
Dim colButtons As Collection
Private Sub UserForm_Activate()
Const CON_HT As Long = 18
Dim x As Long, cbx As MSForms.CheckBox, t
Dim btn As MSForms.CommandButton, txt As MSForms.TextBox
Dim oCheck As clsCheck, oButton As clsButton
Set colCheckBoxes = New Collection
Set colButtons = New Collection
For x = 1 To 10
t = 5 + CON_HT * (x - 1)
Set cbx = Me.Controls.Add("Forms.CheckBox.1", "cbox_" & x)
cbx.Caption = "Checkbox" & x
cbx.Width = 80
cbx.Height = CON_HT
cbx.Left = 5
cbx.Top = t
colCheckBoxes.Add GetCheckHandler(cbx) '<< save in collection
Set btn = Me.Controls.Add("Forms.CommandButton.1", "btnplus_" & x)
btn.Caption = "+"
btn.Height = CON_HT
btn.Width = 20
btn.Left = 90
btn.Top = t
btn.Enabled = False '<<buttons start off disabled
colButtons.Add GetButtonHandler(btn) '<< save in collection
Set btn = Me.Controls.Add("Forms.CommandButton.1", "btnminus_" & x)
btn.Caption = "-"
btn.Height = CON_HT
btn.Width = 20
btn.Left = 130
btn.Top = t
btn.Enabled = False '<<buttons start off disabled
colButtons.Add GetButtonHandler(btn) '<< save in collection
'no events are captured for the textboxes...
Set txt = Me.Controls.Add("Forms.Textbox.1", "txt_" & x)
txt.Width = 30
txt.Height = CON_HT
txt.Left = 170
txt.Top = t
Next x
End Sub
'All "clicked" controls saved in instances of the custom classes
' get passed here. Handle based on control type/name
Public Sub HandleClick(ctrl As MSForms.Control)
Dim num
num = Split(ctrl.Name, "_")(1) 'which set of controls are we working with?
Dim txt As MSForms.TextBox
'get the matching text box...
Set txt = Me.Controls("txt_" & num)
If ctrl.Name Like "cbox_*" Then
If ctrl.Value Then txt.Value = 1
Me.Controls("btnplus_" & num).Enabled = ctrl.Value
Me.Controls("btnminus_" & num).Enabled = ctrl.Value
ElseIf ctrl.Name Like "btnplus_*" Then
txt.Value = txt.Value + 1
ElseIf ctrl.Name Like "btnminus_*" Then
txt.Value = txt.Value - 1
End If
End Sub
'couple of "factory" functions for the event-handling classes
Private Function GetCheckHandler(cb As MSForms.CheckBox)
Dim rv As New clsCheck
Set rv.chk = cb
Set GetCheckHandler = rv
End Function
Private Function GetButtonHandler(btn As MSForms.CommandButton)
Dim rv As New clsButton
Set rv.btn = btn
Set GetButtonHandler = rv
End Function
Sample file: https://www.dropbox.com/s/k74c08m0zkwn9l7/tmpFormEvents.xlsm?dl=0

Use VBA to assign all checkboxes to class module

I'm having a problem assigning VBA generated ActiveX checkboxes to a class module. When a user clicks a button, the goal of what I am trying to achieve is: 1st - delete all the checkboxes on the excel sheet; 2nd - auto generate a bunch of checkboxes; 3rd - assign a class module to these new checkboxes so when the user subsequently clicks one of them, the class module runs.
I've borrowed heavily from previous posts Make vba code work for all boxes
The problem I've having is that the 3rd routine (to assign a class module to the new checkboxes) doesn't work when run subsequently to the first 2 routines. It runs fine if run standalone after the checkboxes have been created. From the best I can tell, it appears VBA isn't "releasing" the checkboxes after they have been created to allow the class module to be assigned.
The below code is the simplified code that demonstrates this problem. In this code, I use a button on "Sheet1" to run Sub RunMyCheckBoxes(). When button 1 is clicked, the class module did not get assigned to the newly generated checkboxes. I use button 2 on "Sheet1" to run Sub RunAfter(). If button 2 is clicked after button 1 has been clicked, the checkboxes will be assigned to the class module. I can't figure out why the class module won't be assigned if just the first button is clicked. Help please.
Module1:
Public mcolEvents As Collection
Sub RunMyCheckboxes()
Dim i As Double
Call DeleteAllCheckboxesOnSheet("Sheet1")
For i = 1 To 10
Call InsertCheckBoxes("Sheet1", i, 1, "CB" & i & "1")
Call InsertCheckBoxes("Sheet1", i, 2, "CB" & i & "2")
Next
Call SetCBAction("Sheet1")
End Sub
Sub DeleteAllCheckboxesOnSheet(SheetName As String)
Dim obj As OLEObject
For Each obj In Sheets(SheetName).OLEObjects
If TypeOf obj.Object Is MSForms.CheckBox Then
obj.Delete
End If
Next
End Sub
Sub InsertCheckBoxes(SheetName As String, CellRow As Double, CellColumn As Double, CBName As String)
Dim CellLeft As Double
Dim CellWidth As Double
Dim CellTop As Double
Dim CellHeight As Double
Dim CellHCenter As Double
Dim CellVCenter As Double
CellLeft = Sheets(SheetName).Cells(CellRow, CellColumn).Left
CellWidth = Sheets(SheetName).Cells(CellRow, CellColumn).Width
CellTop = Sheets(SheetName).Cells(CellRow, CellColumn).Top
CellHeight = Sheets(SheetName).Cells(CellRow, CellColumn).Height
CellHCenter = CellLeft + CellWidth / 2
CellVCenter = CellTop + CellHeight / 2
With Sheets(SheetName).OLEObjects.Add(classtype:="Forms.CheckBox.1", Link:=False, DisplayAsIcon:=False, Left:=CellHCenter - 8, Top:=CellVCenter - 8, Width:=16, Height:=16)
.Name = CBName
.Object.Caption = ""
.Object.BackStyle = 0
.ShapeRange.Fill.Transparency = 1#
End With
End Sub
Sub SetCBAction(SheetName)
Dim cCBEvents As clsActiveXEvents
Dim o As OLEObject
Set mcolEvents = New Collection
For Each o In Sheets(SheetName).OLEObjects
If TypeName(o.Object) = "CheckBox" Then
Set cCBEvents = New clsActiveXEvents
Set cCBEvents.mCheckBoxes = o.Object
mcolEvents.Add cCBEvents
End If
Next
End Sub
Sub RunAfter()
Call SetCBAction("Sheet1")
End Sub
Class Module (clsActiveXEvents):
Option Explicit
Public WithEvents mCheckBoxes As MSForms.CheckBox
Private Sub mCheckBoxes_click()
MsgBox "test"
End Sub
UPDATE:
On further research, there is a solution posted in the bottom answer here:
Creating events for checkbox at runtime Excel VBA
Apparently you need to force Excel VBA to run on time now:
Application.OnTime Now ""
Edited lines of code that works to resolve this issue:
Sub RunMyCheckboxes()
Dim i As Double
Call DeleteAllCheckboxesOnSheet("Sheet1")
For i = 1 To 10
Call InsertCheckBoxes("Sheet1", i, 1, "CB" & i & "1")
Call InsertCheckBoxes("Sheet1", i, 2, "CB" & i & "2")
Next
Application.OnTime Now, "SetCBAction" '''This is the line that changed
End Sub
And, with this new formatting:
Sub SetCBAction() ''''no longer passing sheet name with new format
Dim cCBEvents As clsActiveXEvents
Dim o As OLEObject
Set mcolEvents = New Collection
For Each o In Sheets("Sheet1").OLEObjects '''''No longer passing sheet name with new format
If TypeName(o.Object) = "CheckBox" Then
Set cCBEvents = New clsActiveXEvents
Set cCBEvents.mCheckBoxes = o.Object
mcolEvents.Add cCBEvents
End If
Next
End Sub
If OLE objects suit your needs then I'm glad you've found a solution.
Are you aware, though, that Excel's Checkbox object could make this task considerably simpler ... and faster? Its simplicity lies in the fact that you can easily iterate the Checkboxes collection and that you can access its .OnAction property. It is also easy to identify the 'sender' by exploiting the Evaluate function. It has some formatting functions if you need to tailor its appearance.
If you're after something quick and easy then the sample below will give you an idea of how your entire task could be codified:
Public Sub RunMe()
Const BOX_SIZE As Integer = 16
Dim ws As Worksheet
Dim cell As Range
Dim cbox As CheckBox
Dim i As Integer, j As Integer
Dim boxLeft As Double, boxTop As Double
Set ws = ThisWorkbook.Worksheets("Sheet1")
'Delete checkboxes
For Each cbox In ws.CheckBoxes
cbox.Delete
Next
'Add checkboxes
For i = 1 To 10
For j = 1 To 2
Set cell = ws.Cells(i, j)
With cell
boxLeft = .Width / 2 - BOX_SIZE / 2 + .Left
boxTop = .Height / 2 - BOX_SIZE / 2 + .Top
End With
Set cbox = ws.CheckBoxes.Add(boxLeft, boxTop, BOX_SIZE, BOX_SIZE)
With cbox
.Name = "CB" & i & j
.Caption = ""
.OnAction = "CheckBox_Clicked"
End With
Next
Next
End Sub
Sub CheckBox_Clicked()
Dim sender As CheckBox
Set sender = Evaluate(Application.Caller)
MsgBox sender.Name & " now " & IIf(sender.Value = 1, "Checked", "Unchecked")
End Sub

VBA error 424 when trying to use class method from another module

I have a class called autoCRUD in a class module in excel 2013. From another module (a regular one) I try to call a method from this class and I get the "Object required" exception.
Here's the method:
Public Function CreateCRUDView(TipoCRUD As String) 'TipoCRUD pode ser C (Create), R (Read), U (Update), D (Delete)
Dim myForm As Object
Dim NewFrame As MSForms.Frame
Dim NewButton As MSForms.CommandButton
Dim NewListBox As MSForms.ListBox
Dim NewLabel As MSForms.Label
Dim X As Integer
Dim Line As Integer
Dim t As Integer
Dim arrLeg() As Variant
arrLeg = legenda
'This is to stop screen flashing while creating form
Application.VBE.MainWindow.Visible = False
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
'Create the User Form
With myForm
.Properties("Caption") = "New Form"
.Properties("Width") = 300
.Properties("Height") = 270
End With
'Criar labels
t = 10
For Each lbl In arrLeg
Set NewLabel = myForm.Designer.Controls.Add("Forms.label.1")
With NewLabel
.name = "lbl_" + Replace(CStr(lbl.Value), " ", "")
.t = (10 + t)
.Left = 10
.Font.Size = 8
End With
t = t + 10
Next
'Create CommandButton Create
Set NewButton = myForm.Designer.Controls.Add("Forms.commandbutton.1")
With NewButton
.name = "cmd_1"
If UCase(TipoCRUD) = "C" Then
.Caption = "Salvar"
ElseIf UCase(TipoCRUD) = "U" Then
.Caption = "Alterar"
End If
.Accelerator = "M"
.Top = Top + 10
.Left = 200
.Width = 66
.Height = 20
.Font.Size = 8
.Font.name = "Tahoma"
.BackStyle = fmBackStyleOpaque
End With
Top = Top + 10
End Function
The code from another module that calls the method is :
Public Sub Main()
Dim ac As autoCrud
Set ac = New autoCrud
ac.CreateCRUDView ("c")
End Sub
I don't get it, why am I getting this error?
Here is the code for "legenda":
Public Property Get sht() As Worksheet
Const shtName As String = "Teste1"
Set sht = ActiveWorkbook.Worksheets(shtName)
End Property
Public Property Get legenda() As Range
Const linha As Integer = 3
Const colunaI As Integer = 2
Dim colunaF As Integer
Dim i As Integer
i = colunaI
Do While sht.Cells(linha, i).Value <> ""
i = i + 1
Loop
colunaF = (i - 1)
Set legenda = sht.Range(Cells(linha, colunaI), Cells(linha, colunaF))
End Property
The lbl.Value is supposed to be a string value, the name of the label. And it comes from the spread sheet in the header of the table, teh legenda() only selects that header and the arrLeg takes the legenda as a range and transforms it in an array.
Edit:
Apparently the error occurs in the line that says: .name = "lbl_" + Replace(CStr(lbl.Value), " ", "")
As you can see, I've tried to take the spaces from the string and also ensure that it is a string, but none of it worked.
Edit 2:
I actually just use a class for organization and re-usability purposes. I take the properties and other methods and use them inside the 'createCRUDView' method, this method will then create a CRUD View, that is, create a form either to "Create", "Read" (not used since it's excel),"Update or "Delete" data entries. It basically creates forms dynamically to any table you make
VBA error 424 is object required error. So I'm now pretty sure that lbl in CStr(lbl.Value) is not an object. With your code legenda is a Range but after
Dim arrLeg() As Variant
arrLeg = legenda
arrLeg will be a variant array. This array does not contain objects. You can debug this with
For Each lbl In arrLeg
...
MsgBox TypeName(lbl)
...
Next
So you should use CStr(lbl).
And
Set legenda = sht.Range(Cells(linha, colunaI), Cells(linha, colunaF))
will only work while the "Teste1" sheet is the ActiveSheet because Cells(linha, colunaI) is not explicit assigned to a sheet so the ActiveSheet will be supposed.
Set legenda = sht.Range(sht.Cells(linha, colunaI), sht.Cells(linha, colunaF))
will be better.

Custom right-click menu - OnAction works straight away rather than on button-press

I'm creating a custom menu in Excel which consists of various sub-menus. It's for picking various machinery items and there's about 250 possible outcomes.
In any case, I've got the menu built and want it so that the .Caption is entered into the cell when the menu is used. I've put the .OnAction into the relevant buttons but, unfortunately, the .OnAction activates when the file is opened, not when the button is clicked. As such, all 250-odd .Captions are quickly entered into the same cell in quick succession.
Quick edit - the important bit is towards the bottom of the BuildMenus, where the .OnAction calls the function AddStuff. I know this is running on the Workbook_Activate which is why it runs straight away but everywhere else I've looked online does it the same way.
Private Sub Workbook_Activate()
BuildMenus
End Sub
Private Sub BuildMenus()
'Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Dim AmountOfCats As Integer
Dim ThisIsMyCell As String
ThisIsMyCell = ActiveCell.Address
'this is where we would set the amount of categories. At the moment we'll have it as 15
AmountOfCats = 15
Dim cBut As CommandBarControl
Dim Cats As CommandBarControl
Dim SubCats As CommandBarControl
Dim MenuDesc As CommandBarButton
On Error Resume Next
With Application
.CommandBars("Cell").Controls("Pick Machinery/Plant...").Delete
End With
Set cBut = Application.CommandBars("Cell").Controls.Add(Type:=msoControlPopup, Temporary:=True)
cBut.Caption = "Pick Machinery/Plant.."
With cBut
.Caption = "Pick Machinery/Plant..."
.Style = msoButtonCaption
End With
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim SC As Integer
Dim AmountOfMenus As Integer
SC = 1
Dim MD As Integer
MD = 1
Dim MyCaption As String
For i = 0 To AmountOfCats - 1
Set Cats = cBut.Controls.Add(Type:=msoControlPopup, Temporary:=True)
Cats.Caption = Categories(i + 1)
Cats.Tag = i + 1
For j = 0 To (SubCatAmounts(i + 1) - 1)
Set SubCats = Cats.Controls.Add(Type:=msoControlPopup, Temporary:=True)
SubCats.Caption = SubCatArray(SC)
SubCats.Tag = j + 1
AmountOfMenus = MenuAmounts(SC)
For k = 0 To AmountOfMenus - 1
Set MenuDesc = SubCats.Controls.Add(Type:=msoControlButton)
With MenuDesc
.Caption = MenuArray(MD)
.Tag = MD
MyCaption = .Caption
.OnAction = AddStuff(MyCaption)
End With
MD = MD + 1
Next
SC = SC + 1
Next
Next
On Error GoTo 0
End Sub
Function AddStuff(Stuff As String)
Dim MyCell As String
MyCell = ActiveCell.Address
ActiveCell.Value = Stuff
End Function
OnAction expects a string value: instead you are calling your AddStuff sub while creating your menu...
.OnAction = "AddStuff """ & MyCaption & """"
is what you want (assuming I got my quotes right)
I was making a mistake with my AddStuff - I was calling it as a function when instead it should have been a macro (or a regular sub). A slight modification to Tim Williams' .OnAction code
MyButton.OnAction = "AddStuff(""" & MyButton.Caption & """)"
did the trick.

Resources