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
Related
This may sound like an easy question, but I couldn't find a simple way to write the output of a collection ( just a column ) to a worksheet.
Collection gives the correct answers on debug.print and all I want to do that simply put that output on a worksheet, and then clear the output.
This is my main code for collection;
Worksheets(Ders_Sheet_Adi).Visible = True
Dim LastRowXL_1, LastRowXL_2, LastRowXL_3 As Long
Dim uniques As Collection
Dim Source_XL As Range
LastRowXL_1 = Worksheets(Ders_Sheet_Adi).Cells(Rows.Count, 40).End(xlUp).Row
LastRowXL_2 = Worksheets(Ders_Sheet_Adi).Cells(Rows.Count, 41).End(xlUp).Row
LastRowXL_2_Q = LastRowXL_2 + 1
LastRowXL_3 = Worksheets(Ders_Sheet_Adi).Cells(Rows.Count, 42).End(xlUp).Row
LastRowXL_3_Q = LastRowXL_3 + 1
LastRowXL_4_Q = LastRowXL_3_Q + LastRowXL_1 + 1
XL_Main = WorksheetFunction.Max(LastRowXL_1, LastRowXL_2, LastRowXL_3)
Set Source_XL = Worksheets(Ders_Sheet_Adi).Range("AN2:AP" & XL_Main & "")
Set uniques = GetUniqueValues(Source_XL.Value)
I found a way, just by simply putting a For array after the collection.
I Put these codes after the collection and voila, it works right now;
Dim it_XL
Worksheets(Ders_Sheet_Adi).Range("AN1:AP1100").Select
Selection.ClearContents
it_XLQ = 1
For Each it_XL In uniques
If it_XLQ = 1 Then it_XLQ_M = 100 Else it_XLQ_M = it_XLQ - 1
Worksheets(Ders_Sheet_Adi).Range("AP" & it_XLQ & "") = it_XL
If Worksheets(Ders_Sheet_Adi).Range("AP" & it_XLQ & "") = Worksheets(Ders_Sheet_Adi).Range("AP" & it_XLQ_M & "") Then
Worksheets(Ders_Sheet_Adi).Range("AP" & it_XLQ & "").Delete
GoTo Son2
Else: GoTo Son3
End If
Son3:
it_XLQ = it_XLQ + 1
Next
Worksheets(Ders_Sheet_Adi).Range("AP1:AP20").Copy
Worksheets(Ders_Sheet_Adi).Range("AQ1:AQ20").PasteSpecial Paste:=xlPasteValues
Son2:
LastRow_END = Worksheets(Ders_Sheet_Adi).Cells(Rows.Count, 43).End(xlUp).Row
I have a principal sheet (Launch Tracker) that needs to be updated from a database. I have put the extraction of the database on an adjacent sheet (LAT - Master Data).
What I would like to do is that if the value of the columns H, O, Q are similar then it would replace the lines from column "E" to "AL" on the (Launch Tracker), if there is no match I would like to add the entire line at the end of the (Launch Tracker) sheet.
I already have this code that was running when I made a test, but now it doesn't seem to be working and I cannot figure out why.
Option Explicit
Option Base 1
Dim Ttrak_concat, Tdata_concat, Derlig As Integer
Sub General_update()
Dim Cptr As Integer, D_concat As Object, Ref As String, Ligne As Integer, Lig As Integer
Dim Start As Single
Dim test 'for trials
Start = Timer
Application.ScreenUpdating = False
Call concatenate("LAT - Master Data", Tdata_concat)
Call concatenate("Launch Tracker", Ttrak_concat)
'collection
Set D_concat = CreateObject("scripting.dictionary")
For Cptr = 1 To UBound(Ttrak_concat)
Ref = Ttrak_concat(Cptr, 1)
If Not D_concat.exists(Ref) Then: D_concat.Add Ref, Ttrak_concat(Cptr, 2)
Next
'comparison between the sheets
Sheets("LAT - Master Data").Activate
For Cptr = 1 To UBound(Tdata_concat)
Ref = Tdata_concat(Cptr, 1) 'chaineIPR feuil data
Ligne = Tdata_concat(Cptr, 2) 'localisation sheet data
If D_concat.exists(Ref) Then
Lig = D_concat.Item(Ref) 'localisation sheet track
Else
Lig = Derlig + 1
End If
Sheets("LAT - Master Data").Range(Cells(Ligne, "E"), Cells(Ligne, "AL")).Copy _
Sheets("Launch Tracker").Cells(Lig, "E")
Next
Sheets("Launch Tracker").Activate
Application.ScreenUpdating = False
MsgBox "mise à jour réalisée en: " & Round(Timer - Start, 2) & " secondes"
End Sub
'---------------------------------------
Sub concatenate(Feuille, Tablo)
Dim T_coli, T_colp, T_colr, Cptr As Integer
Dim test
With Sheets(Feuille)
'memorizing columns H O Q
Derlig = .Columns("H").Find(what:="*", searchdirection:=xlPrevious).Row
T_coli = Application.Transpose(.Range("H3:H" & Derlig))
T_colp = Application.Transpose(.Range("O3:O" & Derlig))
T_colr = Application.Transpose(.Range("Q3:Q" & Derlig))
'concatenate for comparison
ReDim Tablo(UBound(T_colr), 2)
For Cptr = 1 To UBound(T_colr)
Tablo(Cptr, 1) = T_coli(Cptr) & " " & T_colp(Cptr) & " " & T_colr(Cptr)
Tablo(Cptr, 2) = Cptr + 2
Next
End With
End Sub
Would someone have the solution to my problem?
Thank you in advance :)
EDIT 11:48
Actually the code runs now but It doesn't work the way I need it to. I would like to update the information on my sheet Launch tracker from the LAT - Master data sheet when the three columns H, O and Q are the same. The problem is that I have checked and some lines present in the LAT - Master Data sheet are not being added into the Launch tracker sheet after running the macro... Does someone have any idea why ?
Agathe
A type mismatch means that you gave a function a parameter that has the wrong type. In your case that means that UBound can't deal with T_colr or ReDim can'T deal with UBound(T_colr). Since Ubound always returns an integer, it must be T_colr.
If Derlig=3 then Application.Transpose(.Range("Q3:Q" & Derlig)) won't return an array but a single value (Double, String or whatever). That's when UBound throws the error.
You will also get an error with T_coli(Cptr) etc.
What you could do to prevent this is to check if Derlig = 3 and treat that case individually.
If Derlig = 3 Then
ReDim Tablo(1, 2)
Tablo(1, 1) = T_coli & " " & T_colp & " " & T_colr
Tablo(1, 2) = 3
Else
ReDim Tablo(UBound(T_colr), 2)
For Cptr = 1 To UBound(T_colr)
Tablo(Cptr, 1) = T_coli(Cptr) & " " & T_colp(Cptr) & " " & T_colr(Cptr)
Tablo(Cptr, 2) = Cptr + 2
Next Cptr
End If
I want to dynamically set the caption for an array of labels (within a VBA form) based on values stored in a worksheet. Thus far I can set them one-by-one like this:
Label1.Caption = MySheet.Range("A1").Value
Label2.Caption = MySheet.Range("B1").Value
Label3.Caption = MySheet.Range("C1").Value ...
Having lots of labels that follow a recurrent pattern, I want to use something smarter, like:
'Method1
For i = 1 To X
Dim MyLabel as Object: Set MyLabel = "Label" & i
MyLabel.Caption = MySheet.Cells(i + 1, i).Value
Next i
'Method2
For i = 1 To X
Label(i).Caption = MySheet.Cells(i + 1, i).Value
Next I
'Both Methods failed. I really appreciate some feedback on this.
Use Controls object
For i = 1 To X
Controls("Label" & i).Caption = MySheet.Cells(i + 1, i).Value
Next
If you want to use this in VBA:
For i = 1 To X
UserForm1.Controls("Label" & i).Caption = MySheet.Cells(i + 1, i).Value
Next
I have the following VBA code which is working well. It's calling another VBA Sub without any trouble:
Public Sub AutoPrintMissingHistoric()
Dim qdf As DAO.QueryDef
Dim rcs As DAO.Recordset
Dim db As DAO.Database
Dim j As Integer
Dim flag As Boolean
Dim i As Long
Dim value_start, value_end As String
Dim tmp As Date
Dim wbRiskedge As Workbook
Dim wsAccueil As Worksheet
Dim wsHistoric As Worksheet
Set wbRiskedge = Workbooks(StrWbRiskedge)
Set wsAccueil = wbRiskedge.Worksheets(StrWsAccueil)
Set wsHistoric = wbRiskedge.Worksheets(StrWsHistoricMissing)
If FistTime = True Then
Call Initialisation.CleanTab
Else
FistTime = True
Call Initialisation.Initialisation
End If
vDelay = 5
Cpt = Cpt + 1
Set db = DBEngine.OpenDatabase(strDB)
Set qdf = db.QueryDefs("Get_missing_fixings")
If Cpt <= wsAccueil.Range(ManualListLetter & "1").End(xlDown).Row Then
Application.StatusBar = wsAccueil.Cells(Cpt, ManualListLetter).Text
qdf.Parameters("arg1") = wsAccueil.Cells(Cpt, ManualListLetter).Value
Set rcs = qdf.OpenRecordset
j = 0
i = 1
flag = False
If Not rcs.EOF Then
rcs.MoveLast
rcs.MoveFirst
While Not rcs.EOF
j = 0
While j < rcs.Fields.Count
If flag = False Then
With Cells(i, j + 1)
If .Value = "" Then
.Value = rcs(j).Name
.Font.Bold = True
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
End If
End With
Else
Cells(i, j + 1).Value = rcs(j).Value
End If
j = j + 1
Wend
If flag = False Then
flag = True
End If
i = i + 1
rcs.MoveNext
Wend
Call ChangeMinMax(rcs.RecordCount, CellMinDate, CellMaxDate, wsHistoric)
Call ParseParameters
Call SetReutersFunction
End If
rcs.Close
qdf.Close
db.Close
wsHistoric.Calculate
Application.StatusBar = wsAccueil.Cells(Cpt, ManualListLetter).Text & " - Next Function: FindMissingValue.AutoFindMissingValue"
sToCall = "FindMissingValue.AutoFindMissingValue"
MTimeGT = Time + TimeValue("00:00:" & vDelay)
Application.OnTime MTimeGT, sToCall
End If
End Sub
I put the execution of this process in a scheduled task. But apparently my code is not well executed: the FindMissingValue.AutoFindMissingValue Sub is not called because Excel just closes.
I think it's because of Application.OnTime MTimeGT, sToCall... What could be the reason?
Here you've the code of FindMissingValue.AutoFindMissingValue
Sub AutoFindMissingValue()
Dim wbRiskedge As Workbook
Dim wsAccueil As Worksheet
Dim wsHistoric As Worksheet
Dim i, nbResult As Long
Set wbRiskedge = Workbooks(StrWbRiskedge)
Set wsAccueil = wbRiskedge.Worksheets(StrWsAccueil)
Set wsHistoric = wbRiskedge.Worksheets(StrWsHistoricMissing)
If Left(wsHistoric.Range(ReutersFormula).Text, 13) Like "Retrieving...*" = True Then
sToCall = "FindMissingValue.AutoFindMissingValue"
MTimeGT = Time + TimeValue("00:00:05")
Application.OnTime MTimeGT, sToCall
Exit Sub
End If
i = WorksheetFunction.CountA(Columns(DateColumn & ":" & DateColumn))
If WorksheetFunction.CountA(Columns(ColumnResearchVResult & ":" & ColumnResearchVResult)) > 0 Then
wsHistoric.Range(FirstCellResearchVResult & ":" & ColumnResearchVResult & WorksheetFunction.CountA(Columns(ColumnResearchVResult & ":" & ColumnResearchVResult))).ClearContents
End If
nbResult = wsHistoric.Range(FirstResult).End(xlDown).Row
wsHistoric.Range(ColumnResearchVResult & LineResearchVResult - 1).Value = "Results"
If WorksheetFunction.CountA(Columns(DateColumn & ":" & DateColumn)) > 1 Then
wsHistoric.Range(FirstCellResearchVResult & ":" & ColumnResearchVResult & i).FormulaLocal = "=RECHERCHEV($" & DateColumn & "$" & LineResearchVResult & ":$" & DateColumn & "$" & i & ";" & FirstLockResult & ":$" & ValueResultColumn & "$" & nbResult & ";2;0)"
End If
Application.StatusBar = wsAccueil.Cells(Cpt, ManualListLetter).Text & " - Next Function: FindMissingValue.AutoPutResultInDb"
sToCall = "FindMissingValue.AutoPutResultInDb"
MTimeGT = Time + TimeValue("00:00:01")
Application.OnTime MTimeGT, sToCall
End Sub
The Application.OnTime part is right and FindMissingValue.AutoFindMissingValue should be called without any problem (after 5 seconds). What might happen is that, during this 5 seconds period, the code continues running, goes back to where AutoPrintMissingHistoric was called from, and the workbook might be closed before these 5 seconds have passed (although, depending upon your exact conditions, the function should be called even despite the workbook is closed).
You can either reduce the waiting period (vDelay = 1, for example) or just call the function directly (Call FindMissingValue.AutoFindMissingValue). Actually, I am not sure why you are calling the function by relying on Application.OnTime; using this is fine for "starting the process" (e.g., "I want my macro to be executed every day at 00:00"), but might drive to "messy situations" in case of being used on a regular basis.
If nothing of this works, please, provide the code of FindMissingValue.AutoFindMissingValue to take a look at it.
NOTE: after some further tests/discussions, I can confirm that the behavior of OnTime under these specific conditions is "too irregular". You should come up with a different approach to allow the waiting period you need or, in case of having to rely on OnTime, do an intensive trial-and-error to make sure that its behaviour is completely under control. This function is expected to be called once (opening the spreadsheet at certain time, for example) and thus you have to pay lots of attention when using it on different contexts (like this one: calling it inside a function).
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