MajP
You've got your good things, and you've got mine.
- Local time
- Today, 08:07
- Joined
- May 21, 2018
- Messages
- 8,904
This demo is in response to a recent thread on creating "custom controls"
https://www.access-programmers.co.uk/forums/threads/is-it-possible-to-create-custom-controls.320996/
Unlike other development environments access does not have a way to make a user defined or custom control. However, but using a custom class you can encapsulate and hide behind the scenes all the complext functionality. You can extend the functions of a single control or more importantly have multiple controls appear to functions as if they are all working together. The trick is to right generic code so that the user never has to look or touch code "behind the scenes".
The true power of a class module is that you can trap all the events of every control of your "custom control". This allows the user to instantiate the class and get all the functionality with just a single line of code to pass in the arguments and initialize the code. There is no copying of code or building your own event procedures.
The way I use my "custom controls" is always the same. I just declare a module level variable of the custom class and initialize it in one line in the form's on load event. So an example would look like this.
That is it. Regardless of the form, or the name of the controls the above is all you need for this example navigation control. You just have to add four command buttons and one textbox. A custom class should be like a black box. There user should not need to understand how it works, and should never have to modify the code to use it. You should provide the inputs and get the outputs.
I will demonstrate building this custom navigation form in two ways.
1. The user creates and places the 4 buttons and textbox on the main form and using a CustomClass
2. The user drops a subform on the mainform that has the buttons and the code.
In the 2nd example I will try to show that a forms module is a class module no different then a custom class, just sometimes we do not think it that way.
This code is not meant to be the perfect navigation control and there are lots of bells and whistles that could be added and error checking. This is just to demonstrate the process. Further there are plenty of examples for doing this with standard modules, and it can easily be done that way. This is to show the ease of reuse and flexibility of a class and how to make a class for a custom control.
In the image there are two "navigation controls". The buttons have different names and are formatted differently, but both as instantiated and initialized as shown above.
Step1: In vba create a new class module. I like to use clean names just like vba. So I do not use and prefix like "cls". Also many of my custom classes have custom collections just like vba (Control, Controls, TableDef / TableDefs) so I use that paradigm for naming as well. I called this "CustomNavigationControl"
Step2: For the controls that make up the custom navigation control, I have to trap their events and the parent form events. At the top of the class I will add a class variable for all controls involved in the custom control and prefix them withevents so that the class can trap their events.
Class variables are almost always private. These are the "properties" of the class. If you want to expose them (which I will not demonstrate here) then you would normally use Let, Get, and Set procedures. However, yes you can make these public and expose but for several reasons that is not normally done. These private variables can be used throughout the class
3. Create an event to pass in your arguments. Normally in a "custom control" I am passing in the controls from the form and other starting arguments. I always call it "initialize", but it can be whatever you want. In this procedure you set you class variables to the arguments.
4. In the initialize ensure that the controls you are referencing will raise the events you need. For a control to raise event it has to have the word "[event procedure]" in the event property. This is the same whenever you build a form. You can have an event procedure, but if the property is blank that procedure will never happen. In this example I know that each button needs to raise the onClick event and the form needs to raise the onCurrent event. I usually have a procedure called configure events, but you could do it all in the initialize. You can trap as many events as you want for each control.
You could also do this manually in the controls. However if you forget to do this or do not do it manually in the controls, you will be very frustrated because nothing will happen and it will be hard to figure out why.
5. If the controls are defined using with events they appear in the drop down and you can easily build event procedures.
I have not yet built the procedures but I know that I want to move in a direction based on the trapped event for a button, and update the record count on the current event of the form
6. The rest of the code really is not related to the techniques of building a class module for a custom control. The rest is just standard code. If you understand the above them you can build a "custom control"
The only thing that is really class related is the use of the "m_ParentForm". Since this is a class level variable set in the initialize procedure it can be used within the class. FYI, a Form is the parent to a control on the form. So in the initialize I just picked one of the buttons to set the m_ParentForm. (Note this will fail if the button is on a tab control and that is not handled in this code)
But here is the code
To use this code on any form drop your buttons on the form. At the top of the form's code declare a CustomNavigationControl variable and call the initialize procedure in the Form's load event.
I will demo the subform in next installment.
https://www.access-programmers.co.uk/forums/threads/is-it-possible-to-create-custom-controls.320996/
Unlike other development environments access does not have a way to make a user defined or custom control. However, but using a custom class you can encapsulate and hide behind the scenes all the complext functionality. You can extend the functions of a single control or more importantly have multiple controls appear to functions as if they are all working together. The trick is to right generic code so that the user never has to look or touch code "behind the scenes".
The true power of a class module is that you can trap all the events of every control of your "custom control". This allows the user to instantiate the class and get all the functionality with just a single line of code to pass in the arguments and initialize the code. There is no copying of code or building your own event procedures.
The way I use my "custom controls" is always the same. I just declare a module level variable of the custom class and initialize it in one line in the form's on load event. So an example would look like this.
Code:
Private NC As CustomNavigationControls
Private Sub Form_Load()
Set NC = New CustomNavigationControls
NC.Initialize Me.cmdFirst, Me.cmdPrevious, Me.cmdNext, Me.cmdLast, Me.txtCount
End Sub
That is it. Regardless of the form, or the name of the controls the above is all you need for this example navigation control. You just have to add four command buttons and one textbox. A custom class should be like a black box. There user should not need to understand how it works, and should never have to modify the code to use it. You should provide the inputs and get the outputs.
I will demonstrate building this custom navigation form in two ways.
1. The user creates and places the 4 buttons and textbox on the main form and using a CustomClass
2. The user drops a subform on the mainform that has the buttons and the code.
In the 2nd example I will try to show that a forms module is a class module no different then a custom class, just sometimes we do not think it that way.
This code is not meant to be the perfect navigation control and there are lots of bells and whistles that could be added and error checking. This is just to demonstrate the process. Further there are plenty of examples for doing this with standard modules, and it can easily be done that way. This is to show the ease of reuse and flexibility of a class and how to make a class for a custom control.
In the image there are two "navigation controls". The buttons have different names and are formatted differently, but both as instantiated and initialized as shown above.
Step1: In vba create a new class module. I like to use clean names just like vba. So I do not use and prefix like "cls". Also many of my custom classes have custom collections just like vba (Control, Controls, TableDef / TableDefs) so I use that paradigm for naming as well. I called this "CustomNavigationControl"
Step2: For the controls that make up the custom navigation control, I have to trap their events and the parent form events. At the top of the class I will add a class variable for all controls involved in the custom control and prefix them withevents so that the class can trap their events.
Code:
Private WithEvents m_FirstButton As Access.CommandButton
Private WithEvents m_PreviousButton As Access.CommandButton
Private WithEvents m_TextRecordCounter As Access.TextBox
Private WithEvents m_NextButton As Access.CommandButton
Private WithEvents m_LastButton As Access.CommandButton
Private WithEvents m_ParentForm As Access.Form
Class variables are almost always private. These are the "properties" of the class. If you want to expose them (which I will not demonstrate here) then you would normally use Let, Get, and Set procedures. However, yes you can make these public and expose but for several reasons that is not normally done. These private variables can be used throughout the class
3. Create an event to pass in your arguments. Normally in a "custom control" I am passing in the controls from the form and other starting arguments. I always call it "initialize", but it can be whatever you want. In this procedure you set you class variables to the arguments.
Code:
Public Sub Initialize(FirstButton As Access.CommandButton, _
PreviousButton As Access.CommandButton, _
NextButton As Access.CommandButton, _
LastButton As Access.CommandButton, _
TextRecordCounter As Access.TextBox)
Set m_FirstButton = FirstButton
Set m_PreviousButton = PreviousButton
Set m_NextButton = NextButton
Set m_LastButton = LastButton
Set m_TextRecordCounter = TextRecordCounter
Set m_ParentForm = m_FirstButton.Parent
'Ensure that events are raised
ConfigureEvents
End Sub
4. In the initialize ensure that the controls you are referencing will raise the events you need. For a control to raise event it has to have the word "[event procedure]" in the event property. This is the same whenever you build a form. You can have an event procedure, but if the property is blank that procedure will never happen. In this example I know that each button needs to raise the onClick event and the form needs to raise the onCurrent event. I usually have a procedure called configure events, but you could do it all in the initialize. You can trap as many events as you want for each control.
Code:
Private Sub ConfigureEvents()
m_FirstButton.OnClick = "[Event Procedure]"
m_PreviousButton.OnClick = "[Event Procedure]"
m_NextButton.OnClick = "[Event Procedure]"
m_LastButton.OnClick = "[Event Procedure]"
m_ParentForm.OnCurrent = "[Event Procedure]"
End Sub
5. If the controls are defined using with events they appear in the drop down and you can easily build event procedures.
I have not yet built the procedures but I know that I want to move in a direction based on the trapped event for a button, and update the record count on the current event of the form
Code:
Private Sub m_FirstButton_Click()
Move "First"
End Sub
Private Sub m_PreviousButton_Click()
Move "Previous"
End Sub
Private Sub m_NextButton_Click()
Move "Next"
End Sub
Private Sub m_LastButton_Click()
Move "Last"
End Sub
Private Sub m_ParentForm_current()
UpdateRecordCounter
End Sub
6. The rest of the code really is not related to the techniques of building a class module for a custom control. The rest is just standard code. If you understand the above them you can build a "custom control"
The only thing that is really class related is the use of the "m_ParentForm". Since this is a class level variable set in the initialize procedure it can be used within the class. FYI, a Form is the parent to a control on the form. So in the initialize I just picked one of the buttons to set the m_ParentForm. (Note this will fail if the button is on a tab control and that is not handled in this code)
But here is the code
Code:
'---------------------------------------------------------- Procedures -----------------------------------------------------------
Private Sub Move(Direction As String)
Dim rs As Recordset
Set rs = m_ParentForm.Recordset
Select Case Direction
Case "First"
rs.MoveFirst
Case "Next"
If GetRecordCount = (rs.AbsolutePosition + 1) Then
MsgBox "Last Record"
Else
rs.MoveNext
End If
Case "Previous"
If rs.AbsolutePosition = 0 Then
MsgBox "First Record"
Else
rs.MovePrevious
End If
Case "Last"
rs.MoveLast
End Select
UpdateRecordCounter
End Sub
Private Function GetRecordCount() As Long
Dim rs As DAO.Recordset
Set rs = m_ParentForm.RecordsetClone
If Not rs.EOF Then
rs.MoveLast
rs.MoveFirst
GetRecordCount = rs.RecordCount
End If
End Function
Private Sub UpdateRecordCounter()
Dim rs As DAO.Recordset
Dim pos As Long
Dim reccount As Long
Set rs = m_ParentForm.Recordset
reccount = GetRecordCount
pos = rs.AbsolutePosition + 1
m_TextRecordCounter.Value = pos & " of " & reccount
End Sub
To use this code on any form drop your buttons on the form. At the top of the form's code declare a CustomNavigationControl variable and call the initialize procedure in the Form's load event.
Code:
Private CustNavCont As New CustomNavigationControls
Private Sub Form_Load()
CustNavCont.Initialize Me.cmdF, Me.cmdP, Me.cmdN, Me.cmdL, Me.txtCount
End Sub
I will demo the subform in next installment.
Attachments
Last edited: