Default300
Board Regular
- Joined
- Jul 13, 2009
- Messages
- 83
Hi. To jump to specific queries re my current issue, see “Objective” and “Questions” headers below.
OVERALL AIM
I have coded a batch print operation for Excel.
It works fine, but now I want to display the list of pending print jobs to the user, and allow them to cancel individual jobs before proceeding.
I want to do this using:
a UserForm that has the appearance of a table;
with multiple rows of data, each one referring to a single job;
with each row / record / job having a unique 3 digit index;
with 7 columns / fields of read only data;
and with cancel / proceed button on each row.
PROGRESS SO FAR
I have created a UserForm with a single row of controls.
The read only data is displayed via 7 Label controls.
Cells in each column will be populated with data from a 2D array (held in a Variant variable).
The array is 1 based in both dimensions. (This seems to happen when populated directly from a Range.)
The array was originally populated from the values of a spreadsheet table.
There are 2 transparent OptionButtons at the end of each row.
There are 2 more Label controls beneath (in Z axis) the OptionButtons, which act as OptionButton captions.
The only active Controls are the 2 OptionButtons.
The Click Event of the “cancel” OptionButton triggers reformatting the row, highlighting rows / jobs which will be cancelled.
The Click Event of the “proceed” OptionButton reformatting the row again to the original appearance.
Values of the “cancel” OptionButtons will be examined when the batch print operation runs.
Controls are named as follows to make it easy to refer to them via the array indices or loop variables:
The 2 digit number that follows the C is the column number of the control.
The 3 digit number that follows the R is the row number of the control. (It will probably be the same as the Array Index, except for the padded zeroes.)
It’s at the end to make it easy to concatenate by appending the index to the root name.
lblDataC01R001
lblDataC02R001
lblDataC03R001
lblDataC04R001
lblDataC05R001
lblDataC06R001
lblDataC07R001
lblCancelC08R001
lblCancelC09R001
optCancelC08R001
optCancelC09R001
The GroupName of 2 OptionButtons in row 001 is grpOptionGroup001
OBJECTIVE
I would like to dynamically replicate this row (ie controls and associated code) as many times as there are “rows” in the first dimension of the array.
Currently, the maximum number of rows required is 51, but it could potentially increase to say 200.
Since the maximum number of rows that I can display on the form at any given time is about 15 to 20, I have contained the rows in a Frame, with a dynamically scaled ScrollBar. If necessary, I suppose this could be changed to a multipage control, though the scrolling frame option seems more scalable.
[Q1] QUESTION
How to Clone Controls and their Event Code?
What is the best way to replicate my “template” row of controls, retaining all properties (except name obviously and horizontal position); and all event code (which would refer to the clone not the template)?
IDEA (1)
I could copy and paste the individual controls, and reposition them correctly, using loops, variables and constants. (If adopting this method, I’d probably do the alignment by column rather than by row.)
IDEA (2)
Would I be better off wrapping all controls in the “row” in another frame (or some other grouping system), and copying and pasting that. An additional benefit of this method would be that (for the reformatting procedure) I could use a For Each loop to iterate through all controls in the Frame, instead of naming all 9 individually.
IDEA (3)
Should I create a “row” Class? If so, I’ll be way out of my comfort zone, though I have created classes in Java with effort.
With any of the methods above, is the Event code of each copied Control encapsulated?
That is, will it be pasted with them?
If not, what is the best way to duplicate it?
I’ve read a little about the CodeModule and CreateEventProc methods.
[Q2] QUESTION
Method to Return Name of Control to which Event Code refers?
Within an Event code procedure, is there a method that returns the name of the control to which the Event code refers?
If so it would enable me to write generic Event code that could be reproduced for each row without any need to edit.
It seems logical to me that the interpreter would know the control name, from the procedure name if nothing else.
I considered the “Me” keyword, but that refers to the UserForm, or sometimes the Frame.
The “Parent” keyword doesn’t seem to help.
Currently, I’m using “ActiveControl” property, via a function I found on the web.
Thanks and credit to Peter Thornton [ Excel VBA Containers vs ActiveControl ]
This works fine for OptionButtons, because they can obtain focus, but when I tried to use it with clickable Labels, it was useless. Also it probably would not work for events which do not trigger a focus change.
CODE (2x Events; 3x Functions)
PS: I know this is a verbose post. I hope that's not a problem. I reckon that those who want to know the crux of the issue can hop to the questions, but those who want to know the full picture (in case my approach is way off) have that info too. Also, it saves all the follow up questions. Lastly, if the approach so far is good, it may help others.
Cheers. TIA.
OVERALL AIM
I have coded a batch print operation for Excel.
It works fine, but now I want to display the list of pending print jobs to the user, and allow them to cancel individual jobs before proceeding.
I want to do this using:
a UserForm that has the appearance of a table;
with multiple rows of data, each one referring to a single job;
with each row / record / job having a unique 3 digit index;
with 7 columns / fields of read only data;
and with cancel / proceed button on each row.
PROGRESS SO FAR
I have created a UserForm with a single row of controls.
The read only data is displayed via 7 Label controls.
Cells in each column will be populated with data from a 2D array (held in a Variant variable).
The array is 1 based in both dimensions. (This seems to happen when populated directly from a Range.)
The array was originally populated from the values of a spreadsheet table.
There are 2 transparent OptionButtons at the end of each row.
There are 2 more Label controls beneath (in Z axis) the OptionButtons, which act as OptionButton captions.
The only active Controls are the 2 OptionButtons.
The Click Event of the “cancel” OptionButton triggers reformatting the row, highlighting rows / jobs which will be cancelled.
The Click Event of the “proceed” OptionButton reformatting the row again to the original appearance.
Values of the “cancel” OptionButtons will be examined when the batch print operation runs.
Controls are named as follows to make it easy to refer to them via the array indices or loop variables:
The 2 digit number that follows the C is the column number of the control.
The 3 digit number that follows the R is the row number of the control. (It will probably be the same as the Array Index, except for the padded zeroes.)
It’s at the end to make it easy to concatenate by appending the index to the root name.
lblDataC01R001
lblDataC02R001
lblDataC03R001
lblDataC04R001
lblDataC05R001
lblDataC06R001
lblDataC07R001
lblCancelC08R001
lblCancelC09R001
optCancelC08R001
optCancelC09R001
The GroupName of 2 OptionButtons in row 001 is grpOptionGroup001
OBJECTIVE
I would like to dynamically replicate this row (ie controls and associated code) as many times as there are “rows” in the first dimension of the array.
Currently, the maximum number of rows required is 51, but it could potentially increase to say 200.
Since the maximum number of rows that I can display on the form at any given time is about 15 to 20, I have contained the rows in a Frame, with a dynamically scaled ScrollBar. If necessary, I suppose this could be changed to a multipage control, though the scrolling frame option seems more scalable.
[Q1] QUESTION
How to Clone Controls and their Event Code?
What is the best way to replicate my “template” row of controls, retaining all properties (except name obviously and horizontal position); and all event code (which would refer to the clone not the template)?
IDEA (1)
I could copy and paste the individual controls, and reposition them correctly, using loops, variables and constants. (If adopting this method, I’d probably do the alignment by column rather than by row.)
IDEA (2)
Would I be better off wrapping all controls in the “row” in another frame (or some other grouping system), and copying and pasting that. An additional benefit of this method would be that (for the reformatting procedure) I could use a For Each loop to iterate through all controls in the Frame, instead of naming all 9 individually.
IDEA (3)
Should I create a “row” Class? If so, I’ll be way out of my comfort zone, though I have created classes in Java with effort.
With any of the methods above, is the Event code of each copied Control encapsulated?
That is, will it be pasted with them?
If not, what is the best way to duplicate it?
I’ve read a little about the CodeModule and CreateEventProc methods.
[Q2] QUESTION
Method to Return Name of Control to which Event Code refers?
Within an Event code procedure, is there a method that returns the name of the control to which the Event code refers?
If so it would enable me to write generic Event code that could be reproduced for each row without any need to edit.
It seems logical to me that the interpreter would know the control name, from the procedure name if nothing else.
I considered the “Me” keyword, but that refers to the UserForm, or sometimes the Frame.
The “Parent” keyword doesn’t seem to help.
Currently, I’m using “ActiveControl” property, via a function I found on the web.
Thanks and credit to Peter Thornton [ Excel VBA Containers vs ActiveControl ]
This works fine for OptionButtons, because they can obtain focus, but when I tried to use it with clickable Labels, it was useless. Also it probably would not work for events which do not trigger a focus change.
CODE (2x Events; 3x Functions)
Code:
[B]Private Sub optCancelC08R001_Click()[/B]
Dim ctlActiveControl As MSForms.Control
Set ctlActiveControl = GetActiveControl()
'(i) Cancel OptionButton = "Yes"
Dim strRowNumber As String
strRowNumber = (Format(Right(ctlActiveControl.Name, 3), "000"))
RowCancellationFormattingOn (strRowNumber)
End Sub
[B]Private Sub optCancelC09R001_Click()[/B]
Dim ctlActiveControl As MSForms.Control
Set ctlActiveControl = GetActiveControl()
'(i) Cancel OptionButton = "No"
Dim strRowNumber As String
strRowNumber = (Format(Right(ctlActiveControl.Name, 3), "000"))
RowCancellationFormattingOff (strRowNumber)
End Sub
[B]Public Function RowCancellationFormattingOn(strRowNumber As String) As Boolean[/B]
'(i) Cancel OptionButton = "Yes"
Dim strRN As String
strRN = strRowNumber
Me.Controls("lblDataC01R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC02R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC03R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC04R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC05R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC06R" & strRN).BackColor = &H8080FF
Me.Controls("lblDataC07R" & strRN).BackColor = &H8080FF
Me.Controls("lblCancelC08R" & strRN).BackColor = &H8080FF
Me.Controls("lblCancelC09R" & strRN).BackColor = &H8080FF
'(i) Confirm Success of Function
RowCancellationFormattingOn = True
End Function
[B]Public Function RowCancellationFormattingOff(strRowNumber As String) As Boolean[/B]
'(i) Cancel OptionButton = "No"
Dim strRN As String
strRN = strRowNumber
Me.Controls("lblDataC01R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC02R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC03R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC04R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC05R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC06R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblDataC07R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblCancelC08R" & strRN).BackColor = &HF1FCFE
Me.Controls("lblCancelC09R" & strRN).BackColor = &HF1FCFE
'(i) Confirm Success of Function
RowCancellationFormattingOff = True
End Function
[B]Private Function GetActiveControl() As MSForms.Control[/B]
(i) [Credit to Peter Thornton]
Dim ctl As MSForms.Control
Set ctl = Me.ActiveControl
Select Case TypeName(ctl)
Case "Frame"
Set GetActiveControl = ctl.ActiveControl
Case "MultiPage"
Set GetActiveControl = ctl.Pages(ctl.Value).ActiveControl
Case Else
Set GetActiveControl = ctl
End Select
End Function
[B]Public Sub Main()[/B]
Dim objUserForm As Object
Set objUserForm = New frmBatchPrintOrder
End Sub
[B]Private Sub UserForm_Initialize()[/B]
With Me.fraData01
.ScrollBars = fmScrollBarsVertical
.ScrollHeight = .InsideHeight * 8.5
End With
End Sub
PS: I know this is a verbose post. I hope that's not a problem. I reckon that those who want to know the crux of the issue can hop to the questions, but those who want to know the full picture (in case my approach is way off) have that info too. Also, it saves all the follow up questions. Lastly, if the approach so far is good, it may help others.
Cheers. TIA.
Last edited: