I'm having a weird problem.
I have a form that looks like the one in the image
Those checkboxes are generated with a peace of code like this one (there is a loop that increments the i and the l ...everything is fine there because the other components are generated through the same piece of code and I have no problem getting their values):
Public Sub AddCboxs(form, masina, nrmasini, replicare, nrcboxs)
Dim i, k, l As Integer
i = 0
l = 1
Do While i < nrmasini
Do While l < nrcboxs + 1
Set cControl = form.Controls("iooly" & i).Add("Forms.CheckBox.1", "sc" & l & "oly" & i, True)
With cControl
.Width = 15
.Height = 16
.Top = 200 + k
.Left = 205
End With
k = k + 35
l = l + 1
Loop
l = 1
k = 0
i = i + 1
Loop
End Sub
Now... I want to do the following thing. If the SC checkbox is checked I want to do some stuff that you'll see in the following piece of code ... without checking the value of the checkbox the code works just fine and does what I want it to do... but the problem is that I need to do it just when the checkbox is checked.
Public Sub CalcOly()
Dim i, j, k As Integer
Dim Rand, ContorVal, ContorTotal As Long
Dim ws As Worksheet
Set ws = Worksheets("Config")
Dim cControl As Control
i = 0
j = 1
ContorVal = 0
Do While i < 5
Do While j < 3
Rand = 30
If raport.Controls("sc" & j & "oly" & i).Value = True Then
Do While ws.Cells(Rand, 1).Value <> "" And Rand < 65536
If ws.Cells(Rand, 1).Value = raport.Controls("combo" & j & "oly" & i).Value Then
Set cControl = raport.Controls("iooly" & i).Add("Forms.Label.1", "valoare" & j & "oly" & i, True)
With cControl
.Caption = Int(ws.Cells(Rand, 2).Value * raport.Controls("q" & j & "oly" & i).Value) & " RON"
.Width = 55
.Height = 14
.Top = 42 + k
.Left = 225
End With
ContorVal = ContorVal + Int(ws.Cells(Rand, 2).Value * raport.Controls("q" & j & "oly" & i).Value)
End If
Rand = Rand + 1
Loop
End If
j = j + 1
k = k + 35
Loop
Set cControl = raport.Controls("iooly" & i).Add("Forms.Label.1", "totalval" & "oly" & i, True)
With cControl
.Caption = ContorVal & " RON"
.Width = 55
.Height = 14
.Top = 350
.Left = 225
End With
k = 0
j = 1
i = i + 1
ContorVal = 0
Loop
End Sub
Now here's the weird thing... if I click on CALCUL VALOARE (which calls the CalcOly procedure) it executes the code but no matter if the SC checkbox is checked or no it shows no value. If I go on page Olympia 4 or Olympia 5 it does what it needs to do but again... ignoring if the SC checkboxes are checked or not.
I tried to get the value of the checkbox in a separate caption and I observed that it doesn't get it... I really don't know why!
Thanks a lot for your help!
Later: http://www.youtube.com/watch?v=mPb617JxgtI I've uploaded a video to see how strange the app acts. I don't get it... if I remove the If that checks if the checkbox is True or False it works fine
You should show us the piece of code that generates the first checkboxes, especially if your other checkboxes work properly.
That said, several things:
In VBA, you can't declare variables this way:
Dim i, j, k As Integer
Dim Rand, ContorVal, ContorTotal As Long
You have to do:
Dim i As Integer, j As Integer, k As Integer
Dim Rand As Long, ContorVal As Long, ContorTotal As Long
See here
Debug
What did you see when debuging your userform. Do the raport.Controls("sc" & j & "oly" & i) exist ?
By the same way, you should have a look at raport.Controls() collection to see what are the elements and their properties.
That would tell you if your Controls were really created the way you wanted.
This seems a bit obscure but from the help for the Add method of the Controls property:
If you add a control at run time, you must use the exclamation syntax
to reference properties of that control. For example, to return the
Text property of a control added at run time, use the following
syntax:
userform1!thebox.text
You could try changing:
If raport.Controls("sc" & j & "oly" & i).Value = True Then
to:
If raport!("sc" & j & "oly" & i).Value = True Then
edit: I'd originally used a separate variable to build up the string but that wouldn't work as the program would look for a control with the same name as the variable rather than the value of the variable
edit2: if that doesn't work you could try:
If raport!"sc" & j & "oly" & i.Value = True Then
but it would depend on the precedence of ! and . relative to &
Just use these for your button click
if checkboxname.value = true then
invoke your function
end if
Related
I'm trying to make a grid of Option Buttons from about 10x60 and would like to do so with VBA, but I can't get the attribute changing to work.
So far I got this:
Sub Buttons()
Dim i As Integer
Dim j As Integer
Dim k As Integer
k = 48
For i = 8 To 9
For j = 5 To 15
ActiveSheet.Shapes.Range(Array("OptionButton" & k)).Select
k = k + 1
Selection.Copy
With Sheets("Weekreview")
.Cells(i, j).Select
.Paste
.Shapes.Range(Array("OptionButton" & k)).Select
.OptionButtons(k).GroupName = i - 1
.OptionButtons(k).LinkedCell = Range(j, i)
End With
Next
Next
End Sub
The problem with this is that the program errors at .OptionButtons(k).GroupName with the message "Unable to get the OptionButtons property of the Worksheet class".
Anyone who can help me?
Edit 1: My first try (before I tried pretty much all the ways I could find googling the issue) was to use Selection.GroupName, this didn't work either. It looks like it can't access the attributes. So either the attribute changing is wrong, or the selection is wrong.
Edit 2: I got the entire program working except the changing of the GroupName of an existing OptionButton. Even though Selection.LinkedCell works, Selection.GroupName doesnt.
Your code copy and paste OptionButton & k then refers to OptionButton & k+1 (object doesn't exist).
Look at line were k is incremented:
k = k + 1
Please change all the words
ActiveSheet.Shapes.Range(Array("OptionButton" & k))
to
ActiveSheet.Shapes.Range("Option Button " & k)
Please try this code:
Sub Buttons()
Dim i As Integer
Dim j As Integer
Dim k As Integer
k = 48
For i = 8 To 9
For j = 5 To 15
ActiveSheet.Shapes.Range(Array("OptionButton" & k)).Select
k = k + 1
Selection.Copy
ActiveSheet.Paste
With Selection
.Name = "OptionButton" & k
.Top = Worksheets("Weekreview").Cells(i, j).Top
.Left = Worksheets("Weekreview").Cells(i, j).Left
.GroupName = i - 1
.LinkedCell = Range(j, i)
End With
Next
Next
End Sub
Controls with a naming convention of TypeName# are ActiveX controls (e.g. "OptionButton1","TextBox1"). The object itself is wrapped in an OLEObject. ActiveX controls on a Worksheet should be references using the Worksheet's OLEObjects collection.
Properties not available directly from the OLEObject can be access by the OLEObject.Object.
Sub Buttons()
Application.ScreenUpdating = False
Dim opt As OLEObject
Dim cell As Range
With Sheets("Weekreview")
For Each cell In Range(Cells(8, 5), Cells(9, 15))
Set opt = .OLEObjects.Add(ClassType:="Forms.OptionButton.1", Link:=False, DisplayAsIcon:=False, Width:=108, Height:=21)
With opt
.Left = cell.Left
.Top = cell.Top
.Width = cell.Width
.LinkedCell = cell
.Name = cell.Address(False, False)
With opt.Object
.GroupName = cell.Row
.Caption = cell.Address(False, False)
End With
End With
Next
End With
Application.ScreenUpdating = True
End Sub
Dim t As Long
Dim u As Long
Dim v As Long
Dim q As Long
Dim p As Long
t = 1
u = 1
Do
If Sheet2.Range("D" & t).Value = "" Then
If Sheet2.Range("D" & t + 1).Value = "" Then
If Sheet2.Range("D" & t + 2).Value = "" Then
If Sheet2.Range("D" & t + 3).Value = "" Then
If Sheet2.Range("D" & t + 4).Value = "" Then
If Sheet2.Range("C" & t).Value = "" Then
Exit Do
End If
End If
End If
End If
End If
End If
If Not Sheet2.Range("D" & t).Value = "" Then
If Not Sheet2.Range("D" & t).Value = "Description" Then
v = Sheet2.Range("A" & 1 & ":" & "A" & t - 1).Height
q = Sheet2.Range("A" & t).Height
p = v + (q / 2) - 5
Set obj = Sheet2.OLEObjects.Add("Forms.checkbox.1")
With obj
.Width = 10
.Top = p
.Left = 875
.Height = 10
End With
u = u + 1
End If
End If
t = t + 1
Loop
This Code will help me to create many active-x check boxes as per my requirement as u can see in the image.
check the image,after i click the necessary check boxes,and then the command button "export the nfr", the row corresponding to the selected check box should be copied to another sheet, is there any way to add codes for that manipulation
sorry for editing the question
https://i.stack.imgur.com/YF2U2.png
Use a "custom" check box, by creating an event sunk class, such as this, clsCustomCheckBox
Option Explicit
Public WithEvents cb As msforms.CheckBox
Public Sub init(cbInit As msforms.CheckBox)
Set cb = cbInit
End Sub
Private Sub cb_Click() ' or the _Change event....
' Your code here
End Sub
You could then add your new ones, afterwards doing something similar to the below
Private c As Collection
Sub testcb()
Dim o As Object
Dim cb As New clsCustomCheckBox
Set o = ActiveSheet.OLEObjects(1)
cb.init o.Object
Set c = New Collection
c.Add cb
End Sub
you could switch to a Form Control instead of an ActiveX one and take advantage of its OnAction property and assign the same sub to all checkboxes
as follows:
Sub Macro2()
Dim t As Long, u As Long, v As Long, q As Long, p As Long
t = 2 '<--| start from 2 otherwise subsequent "A" & (t - 1) would return "A0"!
u = 1
With Sheet2
Do
If WorksheetFunction.CountA(.Cells(t, "D").Resize(5), .Cells(t, "C")) < 6 Then Exit Do
If Not .Cells(t, "D").Value = "Description" Then
v = .Range("A1", "A" & (t - 1)).Height
q = .Cells(t, "A").Height
p = v + (q / 2) - 5
With .CheckBoxes.Add(875, p, 10, 10) '<--| add a 'Form' checkbox
.OnAction = "CheckBoxClick" '<--| current checkbox will "react" calling 'CheckBoxClick()' sub
End With
u = u + 1 '<--| what is this for?
End If
t = t + 1
Loop
End With
End Sub
then you only have to type your CheckBoxClick() sub, for instance:
Sub CheckBoxClick()
With ActiveSheet.CheckBoxes(Application.Caller) '<--| reference caller checkbox
MsgBox "hello from " & .Name & " place at cell " & .TopLeftCell.Address
End With
End Sub
I am running a MonteCarlo Simulation in Excel with VBA but I only receive #Name? errors in the respective cells. When I click into one of these cells, press F2 and then Return the error disappears and the value is properly calculated. What is wrong here?
This is the code line calculating the respective value:
ActiveCell.Formula = "=Start_Rate * EXP(NORM.S.INV(RAND())* Standard_Deviation * (" & i & " ^1/2)) "
And that is the entire code (if necessary):
Sub MC_Simulation()
Dim i As Integer
Dim k As Integer
Dim StartCell As Range
Dim start_row As Integer
Dim start_column As Integer
iterations = Worksheets("Run_MC").Range("MC_Simulations").Value
Duration = Worksheets("Run_MC").Range("Duration").Value
Mean = Worksheets("Run_MC").Range("Mean").Value
Start_Rate = Worksheets("Run_MC").Range("Start_Rate").Value
Standard_Deviation = Worksheets("Run_MC").Range("Standard_Deviation").Value
start_row = 15
start_column = 1
For i = 1 To Duration
For k = 1 To iterations
Worksheets("Run_MC").Cells(start_row, start_column + i).Select
Selection.Value = i
Worksheets("Run_MC").Cells(start_row + k, start_column).Select
Selection.Value = k
Worksheets("Run_MC").Cells(start_row + k, start_column + i).Select
ActiveCell.Formula = "=Start_Rate * EXP(NORM.S.INV(RAND())* Standard_Deviation * (" & i & " ^1/2)) "
'Selection.Value
Next k
Next i
End Sub
You need to take the VBA variables out of the quotations:
ActiveCell.Formula = "=" & Start_Rate & "*EXP(NORM.S.INV(RAND())*" & Standard_Deviation & "*(" & i & "^1/2))"
I am trying to write a macro which search data from one sheet and copy's to another.
But now I have a problem because I want to copy data between two searches and paste the whole data from multiple cells into one single cell.
For example in the above picture my macro:
SEARCH for "--------------" and "*****END OF RECORD"
COPIES everything in between , here example data in row 29 and 30 and from column A,B,C
PASTE all the data from multiple cells A29,B29,C29 and then A30,B30,C30 to single cell in sheet 2 say cell E2.
This pattern is reoccurring in the column A so I want to search for the next occurrence and do all the steps 1,2,3 and this time I will paste it in Sheet2 , cell E3.
Below is the code:
I am able to search my pattern but hard time in giving references to the cells in between those searched patterns and then copying all the data to ONE cell.
x = 2: y = 2: Z = 7000: m = 0: n = 0
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "---------------------" Then m = x
If ThisWorkbook.Sheets("lic").Range("A" & x) = "****** END OF RECORD" Then n = x
If (n > 0) Then
Do
For i = m To n
ThisWorkbook.Sheets("lic").Range("A" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("B" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("C" & i + 1).Copy
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y) = ThisWorkbook.Sheets("lic").Range("A" & m + 1, "C" & n - 1): y = y + 1
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y).Resize(CopyFrom.Rows.Count).Value = CopyFrom.Value: y = y + 1
Loop While Not x > Z
'Driver's Licence #:Driver's Licence #:Driver's Licence #:
x = 2: y = 2: Z = 7000: counter = 1
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "Driver's Licence #:" Then counter = counter + 1
If (counter = 2) Then ThisWorkbook.Sheets("Sheet1").Range("B" & y) = ThisWorkbook.Sheets("lic").Range("C" & x): y = y + 1: counter = 0
If x = Z Then Exit Sub
Loop
End Sub
Considering that the search is working correctly, about the copy thing you just need to do:
Sheet2.Range("E2").value = ThisWorkbook.Sheets("lic").Range("A" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("B" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("C" & i + 1).value
The result will be something like: AIR COO; L DAT; A
--------UPDATE---------
It was hard to understand your code, so I'm write a new one. Basically it's copy what it found on sheet1 to sheet2.
Sub Copy()
Dim count As Integer 'Counter of loops to the for
Dim Z As Integer 'Limit of (?)
Dim h As Integer 'Count the filled cells on sheet2
Dim y As Integer 'Counter the columns to be copied
Z = 7000
h = 1
'Assuming that the "----" will always be on the top, the code will start searching on the second row
'if it's not true, will be needed to validate this to.
For count = 2 To Z
If Sheet1.Cells(count, 1).Value <> "****** END OF RECORD" Then
If Sheet1.Cells(count, 1).Value <> "" Then
For y = 1 To 3 'In case you need to copy more columns just adjust this for.
Sheet2.Cells(h, 1).Value = Sheet2.Cells(h, 1).Value & Sheet1.Cells(count, y).Value
Next y
h = h + 1
End If
Else
MsgBox "END OF RECORD REACHED"
Exit Sub
End If
Next count
End Sub
Maybe I don't get the full idea but this might work for you.
I'm not at all sure what you want to see in the final output, so this is an educated guess:
Sub DenseCopyPasteFill ()
Dim wsFrom, wsTo As Worksheet
Dim ur As Range
Dim row, newRow As Integer
Dim dataOn As Boolean
Dim currentVal As String
dataOn = False
newRow = 3
Set wsFrom = Sheets("Sheet1")
Set wsTo = Sheets("Sheet2")
Set ur = wsFrom.UsedRange
For row = 1 To ur.Rows.Count
If wsFrom.Cells(row, 1).Value2 = "--------------" Then
dataOn = True
ElseIf wsFrom.Cells(row, 1).Value2 = "***** END OF RECORD" Then
newRow = newRow + 1
dataOn = False
ElseIf dataOn Then
currentVal = wsTo.Cells(newRow, 5).Value2
wsTo.Cells(newRow, 5).Value2 = currentVal & _
wsFrom.Cells(row, 1) & wsFrom.Cells(row, 2) & _
wsFrom.Cells(row, 3)
End If
Next row
End Sub
If you can get away without using the Windows clipboard, I would. Instead of copy/paste, here I demonstrated how you can simply add or append a value.
Add this sub:
Sub copy_range(rng As Range)
Dim str As String
str = rng.Cells(1).Value & rng.Cells(2).Value & rng.Cells(3).Value
Range("E" & Range("E" & Rows.Count).End(xlUp).Row + 1).Value = str
End Sub
Then your for loop should look like this:
For i = m To n
copy_range ThisWorkbook.Sheets("lic").Range("A" & i + 1 & ":C" & i + 1)
Next i
I am trying to create a userform whereby people can place as many requests as they want with the ability to remove requests they no longer want, as well as other functionalities. I am having an issue with removing the dynamically created objects after the functions add-remove-add has been used in that sequence.
The code I have below has a snippet of the objects added to the userform along with dimension changes to the userform and objects already embedded in the userform. Other defined portions are not included below.
Dim RemoveButtonArray() As New Class_RemoveRequest
For i = Last To Last
Set AddRemoveButton = GenPurchaseRequest.Controls.Add("Forms.Image.1", "btnRemove" & ObjID)
With AddRemoveButton
'properties
End With
Set AddRemoveLabel = GenPurchaseRequest.Controls.Add("Forms.Label.1", "lblRemove" & ObjID)
With AddRemoveLabel
'properties
End With
Set AddRequest = GenPurchaseRequest.Controls.Add("Forms.Frame.1", "Frame" & ObjID)
With AddRequest
'properties
.Caption = "Purchase Request - " & ObjID
End With
With AddRequestButton
.Top = 168 + (126 * i)
.Left = 18
End With
With SubmitButton
.Top = 168 + (126 * i)
.Left = 200
End With
With CancelButton
.Top = 168 + (126 * i)
.Left = 381
End With
With GenPurchaseRequest
.ScrollHeight = 200 + (126 * i)
.ScrollTop = 200 + (126 * i)
End With
ReDim Preserve RemoveButtonArray(0 To i)
Set RemoveButtonArray(i).RemoveButton = AddRemoveButton
Next i
ObjID = ObjID + 1
Last = Last + 1
This works well and the form is populated with everything correctly. When the user removes a request, the below code works fine as well:
Public WithEvents RemoveButton As MSForms.Image
Private Sub RemoveButton_click()
Dim ConfirmRemoval As Integer
Dim rbRefNo As String
Dim rbRefNoConvert As Integer
ConfirmRemoval = MsgBox("Are you sure you would like to remove this request?", vbYesNo)
If ConfirmRemoval = vbYes Then
rbRefNo = Mid(Me.RemoveButton.Name, 10)
rbRefNoConvert = CInt(rbRefNo)
With GenPurchaseRequest
If Last > 1 Then
.Controls.Remove ("Frame" & rbRefNo)
.Controls.Remove ("btnRemove" & rbRefNo)
.Controls.Remove ("lblRemove" & rbRefNo)
For i = rbRefNoConvert + 1 To Last - 1
.Controls("Frame" & i).Top = .Controls("Frame" & i).Top - 126
.Controls("btnRemove" & i).Top = .Controls("btnRemove" & i).Top - 126
.Controls("lblRemove" & i).Top = .Controls("lblRemove" & i).Top - 126
Next i
.AddRequestButton.Top = .AddRequestButton.Top - 126
.SubmitButton.Top = .SubmitButton.Top - 126
.CancelButton.Top = .CancelButton.Top - 126
.ScrollTop = .ScrollTop - 126
.ScrollHeight = .ScrollHeight - 126
Last = Last - 1
Else
MsgBox "There is only one active Purchase Request."
End If
End With
Else
'do nothing
End If
End Sub
The user can then go back add additional requests as well as remove more requests that they no longer want. The problem arises when they add more requests and then attempt to remove the last one added directly after the removal. For example: I added 4 requests and then removed the 2nd one. I then added another request, but wanted to remove the 4th request, however, the remove button no longer works.
I believe that the issue is that I need to redefine the array used to store the removal buttons once the remove button function is called, however I have no idea how to do that. My current attempt at doing that is:
For j = 0 To Last
If j = rbRefNoConvert Then
j = j + 1
Else
ReDim RemoveButtonArray(0 To j)
Set RemoveButtonArray(j).RemoveButton = AddRemoveButton
End If
Next j
But that object reference is incorrect and I do not know how to reference it correctly. I tried referencing the control itself, but that did not work.
I am very new to the use of class modules, arrays, and dynamic userforms, so sorry for the lengthy question!
Any help would be very much appreciated!
I attempted a couple of things:
(1) Setting the reference to the control I wanted to delete as nothing in the array.
(2) Adding the controls to a collection rather than a dynamic array.
None of the above worked, so instead I used this last method.
(3) I cleared the text values of the controls that needed to be removed. Then, using a for loop, I moved all the text values of the controls after the ones I wanted to delete to the above frames. Then, I removed the last control, redefined the array (when the user clicks the button again to add another set of controls), and redefined my counter. The code is represented below.
Public WithEvents RemoveButton As MSForms.Image
Private Sub RemoveButton_click()
'Defines appropriate variables
Dim ConfirmRemoval As Integer
Dim rbRefNo As String
Dim rbRefNoConvert As Integer
'Asks user for input to remove a control
ConfirmRemoval = MsgBox("Are you sure you would like to remove this request?", vbYesNo)
If ConfirmRemoval = vbYes Then
'Extracts the name identifier from the control to be removed and also converts it into a number
rbRefNo = Mid(Me.RemoveButton.Name, 10)
rbRefNoConvert = CInt(rbRefNo)
With GenPurchaseRequest
If ObjID > 1 Then
'Loops through the dynamic form controls and adjusts the user-inputs to account for the removed control
For i = rbRefNoConvert To ObjID - 1
If i < (ObjID - 1) Then
.Controls("txtVendor" & i).Text = .Controls("txtVendor" & i + 1).Text
.Controls("txtItem" & i).Text = .Controls("txtItem" & i + 1).Text
.Controls("txtQuantity" & i).Text = .Controls("txtQuantity" & i + 1).Text
.Controls("txtProject" & i).Value = .Controls("txtProject" & i + 1).Value
.Controls("txtCatalog" & i).Text = .Controls("txtCatalog" & i + 1).Text
.Controls("txtDate" & i).Value = .Controls("txtDate" & i + 1).Value
Else
.Controls("txtVendor" & i).Text = .Controls("txtVendor" & i).Text
.Controls("txtItem" & i).Text = .Controls("txtItem" & i).Text
.Controls("txtQuantity" & i).Text = .Controls("txtQuantity" & i).Text
.Controls("txtProject" & i).Value = .Controls("txtProject" & i).Value
.Controls("txtCatalog" & i).Text = .Controls("txtCatalog" & i).Text
.Controls("txtDate" & i).Value = .Controls("txtDate" & i).Value
End If
Next i
'Removes selected remove button and associated form controls
.Controls.Remove ("Frame" & ObjID - 1)
.Controls.Remove ("AddRequestOptions" & ObjID - 1)
'Re-formats userform to adjust for removed controls
.AddRequestButton.Top = .AddRequestButton.Top - 126
.CopyRequestButton.Top = .CopyRequestButton.Top - 126
.SubmitButton.Top = .SubmitButton.Top - 126
.CancelButton.Top = .CancelButton.Top - 126
.ScrollTop = .ScrollTop - 126
.ScrollHeight = .ScrollHeight - 126
'Adjusts the object identifier variable to account for removed control
ObjID = ObjID - 1
Else
MsgBox "There is only one active Purchase Request."
End If
End With
Else
'do nothing
End If
End Sub