I'm Back with a Challenge (custom controls) (2 Viewers)

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
Prior to going into time-out some people showed interest in custom classes (@Tera ,@moke123 ) and learning how to simulate user controls with custom classes.

There is not a native spinner in Access but you can use the MSFORMS active x control MSFORMS controls are pretty stable and portable, however; using any non-native control can always be an issue. So you can build a very good simulated custom spinner control which will be even more flexible than the MSFORMS control. This will be a very good first first custom class. It then shows you how portable the custom classes are. It is really then two lines of code to use the class for any "spinner". The way to learn custom classes and in general OOP concepts is to dive in and build a few.
So if interested the challenge is to build this custom class and see if you can post a solution. If you need help reply back.



Properties
UpButton as command button
DwnButton as command Button
TextBox as textbox
Form as form
SpinnerValue as double
Increment as double
minvalue as double
maxvalue as double
defaultvalue as double

Public Method
InitializeSpinner
signature:
Code:
Public Sub InitializeSpinner(CommandUp As Access.CommandButton, CommandDown As Access.CommandButton, SpinnerTextBox As Access.TextBox, Optional Increment = 1, Optional ByVal minValue As Double = -99999999, Optional ByVal maxvalue As Double = 999999999, Optional ByVal DefaultValue As Double = 0)

Need to capture the events of
CommandUp click
CommandDown click
TextBox afterupdate 'if user types in the text box need to synch spinner
Form current 'need to set spinner value to current textbox value

Optionally raise an event when the spinner changes

Hint: Here are the private variables and custom event signature
Code:
Private WithEvents mCmdUp As Access.CommandButton
Private WithEvents mCmdDwn As Access.CommandButton
Private WithEvents mTextBox As Access.TextBox
Private WithEvents mForm As Access.Form

Private mSpinnerValue As Double
Private mIncrement As Double
Private mMaxValue As Double
Private mMinValue As Double
Private mDefaultValue As Double

Public Event Change(SpinnerValue As Double)
Hint 2. Here is the code in the demo to initialize and use the 4 spinners

Code:
Private spinInt As AccessSpinner
Private spinInt2 As AccessSpinner
Private spinDbl As AccessSpinner
Private spinDate As AccessSpinner

Private Sub Form_Load()
  Set spinInt = New AccessSpinner
  Set spinInt2 = New AccessSpinner
  Set spinDbl = New AccessSpinner
  Set spinDate = New AccessSpinner

  spinInt.InitializeSpinner Me.cmdUpInt, Me.cmdDownInt, Me.IntegerField, 1, 0, 100
  spinInt2.InitializeSpinner Me.cmdUpInt2, Me.cmdDwnInt2, Me.IntegerField2, 5, -20, 20
  spinDbl.InitializeSpinner Me.cmdUpDbl, Me.cmdDwnDbl, Me.DoubleField, 0.1
  spinDate.InitializeSpinner Me.cmdUpDate, Me.cmdDwnDate, Me.DateField, , 1, CDbl(#12/31/2050#), Date
End Sub





I have provided the ACCDE for demo. (Not sure about this new site but would not allow the .accde extension. So I renamed to .txt. I will have to figure out how to post a db)

hood.jpg
timeOUt.jpg
 

Attachments

  • CustomSpinner2.txt
    572 KB · Views: 21
Last edited:

NauticalGent

CopyPaster of the First Order
Local time
Yesterday, 20:47
Joined
Apr 27, 2015
Messages
3,083
Welcome back, challenge accepted. Doubt I'll finish first, but stranger things have happened!
 

deletedT

Guest
Local time
Today, 01:47
Joined
Feb 2, 2019
Messages
1,220
@MajP welcome back. I really missed you and your posts here. Unfortunately I had a terrible week with my job and I wasn't able to keep my promise. But tomorrow I'm free for several hours. This kind of challenge is out of my reach but I will start my study with your sample databases and will have an eye here to see what others have to share.

thanks for your time and putting it up here.
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
Welcome back my friend.
Looks like you just interupted my workday.:)
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
ok Professor, here's my first attempt.
I couldn't lock it down, so no cheating Tera or NG.
I only did one spinner cause I am supposed to be working after all.
 

Attachments

  • MajPChallenge.zip
    34.3 KB · Views: 22

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
Pretty good, but in your form the only code should be this.
Code:
Private clsT As clsSpinner

Private Sub Form_Load()
  Set clsT = New clsSpinner
  clsT.initSpinnerInt Me, Me.CommandUP, Me.CommandDOWN, Me.IntegerField, 1, 0, 100, 50
End Sub

What you are missing is trapping the control events in the class, that is why those variables are declared withevents

Code:
Private Sub m_ctlcmdDown_Click()

End Sub

Private Sub m_ctlcmdUP_Click()

End Sub

Private Sub m_ctlTextBox_AfterUpdate()

End Sub

Private Sub mFrm_Current()

End Sub

Also mFrm needs to be declared as a Form. The way you had it would only allow the class to work with your Form2 and no other.
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
Yea that makes sense. I've been trying so many things with withevents and raiseevents recently which is probably why I went that way.
Ill give it another shot when I get home. No distractions like having to actually work.
No consolation prize for it actually working?
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
That was a quick easy fix ( I think).
Hows this version look?

Edit: after posting I went back and added another field and spinner,initialized and BOOM, it just worked :cool:
 

Attachments

  • MajPChallenge (2).zip
    34 KB · Views: 18
Last edited:

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
You got all the pieces as far as class modules go. There may be a few logic tweaks, but that not that important for the purpose of this. Bottom line I could easily add another spinner in a matter of seconds, and that is the power of this type of coding.
Code:
Dim clsT As clsSpinner
Dim clsD As New clsSpinner

Private Sub Form_Load()
  Set clsT = New clsSpinner
  clsT.initSpinnerInt Me, Me.CommandUP, Me.CommandDOWN, Me.IntegerField, 1, 0, 100, 50
  clsD.initSpinnerInt Me, Me.cmdDateUp, Me.cmdDateDown, Me.DateField, 1, CDbl(#1/1/2000#), CDbl(#1/1/2100#), Date
End Sub

A couple of things
1. You did a good job of in the terminate event of setting the class variable objest to nothing. People always say to set things to nothing, and I would argue this is the only time that it is really needed. Even then it is only a precaution. You have what is called a composite class. A class that has other objects as members of the class. If you blow away the top class (clsSpinner) you want to make sure the object properties are set to nothing.
2. I do not like calling my classes with a prefix like cls. I like it real descriptive. Personal preference for searching for a class.
3. I provided some optional arguments. If not familiar with this learn this because if you think of it how many Access objects have optional parameters. I recommend if you make it optional you provide a default
instead of
optional DefaultValue as Double
I usually put
Optional DefaultValue as Double = 0

Logic wise the big difference is when the text field was null I set the value of the spinner and not the textbox. Then if I change the spinner it updates the textbox. The way you have it set up, you basically are entering a new record without any user input. Again that is just window dressing, you got the basics.
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
Logic wise the big difference is when the text field was null I set the value of the spinner and not the textbox. Then if I change the spinner it updates the textbox. The way you have it set up, you basically are entering a new record without any user input. Again that is just window dressing, you got the basics.
I was aware of that. I believe at one point I had it your way but I think I changed it when I was having difficulty with the oncurrent event.
All in all I have been having a blast with this stuff the last couple weeks.
 

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
That is one of the benefits of OOP, encapsulation. The sausage making is hidden and you have access to the public properties. Assume I have a property like Amount_Due, and I have some complicated way to figure this out. You use this class and call this property often. Then you determine that there are some bugs in the logic and update the class. The user updates the class and their code that calls the class will still work. Because the property accessor "amount_Due" stays the same. The hidden sausage making is what changes.
 

deletedT

Guest
Local time
Today, 01:47
Joined
Feb 2, 2019
Messages
1,220
A while back I was looking for how I can use a class within another one. I searched google and came to a post that used a class to access the properties of all controls in a form. It was interesting for me and a good study to go through it.
In case you want to have a look :
 

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
A while back I was looking how I can use a class within another one. I searched google and came to a post that used a class to access the properties of all controls in a form. It was interesting for me and a good study to go through it.
In case you want to have a look

Maybe I am not seeing it, but to me those are just horrible examples. If I was not versed in custom classes the first thing would be "So what?". That looks like writing code to write code. I can not see the utility. I want to write classes once and reuse them everywhere. They wrote classes that did one thing for the specific control. It would be far easier to write this in the form's code. I will relook at it, but I do not get what they are trying to do.
 

NauticalGent

CopyPaster of the First Order
Local time
Yesterday, 20:47
Joined
Apr 27, 2015
Messages
3,083
@moke123 , nicely done. I'm afraid I took a peek so anything i do is simply plagiarism.

@MajP , thanks again for posting this. Class modules are a very challenging topic for me and although I understand the concept, I had difficulty trying to see the value added in most cases. Your examples have help me see it...

This is an Blog I read last year that I found very helpful for those of us that are a little slow:

 
Last edited:

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
Thanks NG. (confession : I tried to cheat but @MajP locked it down so I couldn't see his code)

Class modules are a very challenging topic for me and although I understand the concept, I had difficulty trying to see the value added in most cases. Your examples have help me see it...
Agreed. I've tried many times over the years to get a grip on classes and withevents but usually grew frustrated and moved on. Something about MajP's examples just clicked and I haven't been able to stop experimenting since.
I tend to use a fair amount of unbound forms for some processes as a control thing. I can see how what I'm learning now will really be a benefit . The downside is now I have to go back and re-write a lot of old code.
 

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
I tend to use a fair amount of unbound forms for some processes as a control thing.
@moke123 I accept the challenge, to try to build the class UnboundController. As I have said many times I am not a fan of unbound forms, but this would be a great learning point to demonstrate some other concepts such as custom collections. Often people will make a class to support a specific unbound form as described here by @pbaldy

This is likely going to be a try and fail because I think there may be just to many exceptions to make it truly generic as described here:

However, that will be good. You should be able to add and edit properties and procedures which will demonstrate encapsulation (change the guts and not the accessors). I am thinking the actual amount of class properties and methods will be few, the code inside the methods will be lengthy and complicated. I will start a new thread and add to it as I go to demonstrate the thought process.

To keep it simple will make it for Access tables only. If that works then modify it for SQL back end. Might be too complicated to have one class for both. In my mind to start:

Properties
UnboundControls : A custom collection of UnboundControl. An UnboundControl has a reference to a control and a "controlSource"
SourceDomain: where values come from
DestinationDomain: if different from source where to add, delete

Methods
LoadFromPK : Load form from primary key
Add : Adds new record
Delete: Deletes
Update: Update
GetNext
GetPrevious

@Tera I relooked at those examples and saw that they caveat that the examples where purely to demonstrate the concepts and not something you would use as is. So that makes more sense.
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
I cant access the article but sounds really interesting. Looking forward to this next lesson.
 

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
@MajP
I had the choice today to clean the house or tinker some more with the classes so I didn't clean the house.

The only thing I could think of to do was make a custom date picker with 0 code in the form module.
All the formatting and events are handled in the class module. There are 45+ controls on the form so it may not look too pretty.
Is there a better way handle that many controls as far as the declarations, the set statements, and click events,etc? I did a loop to set the OnClick = "[Event Procedure]" which works.

If you can take a look, let me know what you think.

Edit: I just noticed something . I added a msgbox to the Class_terminate sub . I added a call to it in the date picker form close event and it does fire. But it also fires again if you click to invoke the datepicker again. Any thoughts?
 

Attachments

  • DatePicker.zip
    87.3 KB · Views: 16
Last edited:

MajP

You've got your good things, and you've got mine.
Local time
Yesterday, 20:47
Joined
May 21, 2018
Messages
3,683
I will take a look when I get a chance.
Is there a better way handle that many controls as far as the declarations, the set statements, and click events,etc?
Going back to my thread on events
In access the easiest to do is to build a function that traps multiple events.

But that is not going to work with a class. So you asked. I have never shown this because this is not OOP 101. This is graduate level OOP.
So lets assume you build a custom class that handles all the calendar events and properties. You would have as one of the class properties a collection of controls (textboxes) and in the class you need to know when one of those is clicked. What you do not want to do is make 45+ controls withevents. As I said before modern languages can trap multiple events in one sub. Not doable in VBA.

So I built a class called commonControl where I trap the event, and the commoncontrols are stored in a collection commonControls. The big trick here is that the control tells the collection that an event took place and the Collection raises a custom event so that another class can trap it. If your head is not hurting, mine is. So now a class that has a commoncontrols as a property can react to its events (which is one of the controls in the collection event). So basically I am bubbling things up to the collection. If this does not make sense do not worry, I wrestled with this for a long time.

Here is my calendar control. It looks like a custom class because a form module is a class. I just instantiate it more like a custom class. What is unique here is it can be used as a form control or a pop up. This does not use a custom collection
 

Attachments

  • ContainedCalendarControl v2.zip
    99.6 KB · Views: 23
  • CommonControls2.zip
    36.4 KB · Views: 23

moke123

AWF VIP
Local time
Yesterday, 20:47
Joined
Jan 11, 2013
Messages
1,949
This is graduate level OOP.
I have high aspirations ;)
Where can I buy your book? You make this sound easy.
In access the easiest to do is to build a function that traps multiple events.
Would this entail rather than assigning - controlevent = [Event Procedure], I would use something like - controlevent = SomeFunction and capture which control it was with screen. ActiveControl in the function?

At this point my head is popping. I've been at this all day. I'll be re-reading this stuff in the morning. I wanna at least get to the oop 101 level.
 

Users who are viewing this thread

Top Bottom