Hello all !
This thread is intended to be a discussion.
There are many tutorials about how to create a class or an OCX.
So should not be a problem for me to do this.
But I never used this tools because (I think) I am not able to "see" the situations when a tool like this help me to design better programs.
So, be kind, please, and share with me from your experience.
Where this tools should be used ? Where you created a new class or an OCX ? Why you decided to do this ?
While I never used my own classes / OCXs I am sure that I don't know even what should I ask about this subject.
So feel free to answer me also to the questions that I can not formulate
I'll take a crack at this. Might take a few iterations, we'll see. For starters, take a look at this type. Have you ever used types in VBA? And if not, maybe experiment with/read about types.
Code:
Public Type AddressType
AddressID As Long
Street1 As String
Street2 As String
City As String
Province As String
Country As String
PostCode As String
End Type
With that declaration, you can now declare variables of that type . . .
Code:
Dim MyAddressInstance As AddressType
. . . and assign values to that variable . . .
Code:
Dim MyAddress As AddressType
With MyAddress
.Street1 = "The White House"
.Street2 = "1600 Pennsylvania Avenue"
.City = "Washington"
.Province = "DC"
.Country = "USA"
.PostCode = "20500"
End With
. . . and that's fairly powerful now because we can pass that variable around to other code, like, . . .
Code:
Sub PrintEnvelope(Address As AddressType)
[COLOR="Green"]' This routine prints the given address to an envelope
'. . . printing code[/COLOR]
End Sub
But it's tedious filling the type with data, so we can write code for that . . .
Code:
Function LoadAddress(AddressID As Long) As AddressType
[COLOR="Green"]' Returns a structured AddressType variable loaded
' with data from the table[/COLOR]
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset( _
"SELECT * FROM tAddress " & _
"WHERE AddressID = " & AddressID)
With rst
If Not .EOF Then
LoadAddress.AddressID = !AddressID
LoadAddress.Street1 = !Street1
LoadAddress.Street2 = !Street2
LoadAddress.City = !City
LoadAddress.Province = !Province
LoadAddress.Country = !Country
LoadAddress.PostCode = !PostCode
End If
.Close
End With
End Function
. . . and we can write code to save that data back to the table . . .
Code:
Sub SaveAddress(Address As AddressType)
[COLOR="Green"]' Receives a structured AddressType variable
' and attempts to save that data back to the table[/COLOR]
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset( _
"SELECT * FROM tAddress " & _
"WHERE AddressID = " & Address.AddressID)
With rst
If .EOF Then
.AddNew
!AddressID = Address.AddressID
Else
.Edit
End If
!Street1 = Address.Street1
[COLOR="Green"] ' . . .
[/COLOR] .Update
.Close
End With
End Sub
. . . and as our application grows we might write other functions that we can invoke using the data in our AddressType type, like maybe we open a browser, navigate to google, and show a map of the address . . .
Code:
Sub ShowGoogleMapOf(Address as AddressType)
[COLOR="Green"]' This routine opens a browser, navigates to google maps, writes this address
' into the search bar, and effectively shows a map of the address[/COLOR]
[COLOR="Green"] ' code goes here[/COLOR]
End Sub
. . . so to keep all this related Address functionality together we write them all in a standard module called mAddress. Fine.
Now that's a fair bit of code, and at first glance makes it seem harder to work with address data, but wait. Now that that code is all written, we can get at, edit, and save address data very very quickly, like . .
Code:
[COLOR="Green"]'declare the variable[/COLOR]
Dim MyAddress as AddressType
[COLOR="Green"]'read the data from the table[/COLOR]
MyAddress = LoadAddress(51)
[COLOR="Green"]'make an edit[/COLOR]
MyAddress.PostCode = "26047"
[COLOR="Green"]'and save the changes[/COLOR]
SaveAddress MyAddress
So, I'm out of time, but here's an assignment if you're interested in more:
1) Copy all this code into a new database, into a standard module called mAddress.
2) Create the table described in LoadAddress, and put some sample data in there.
Then we'll use that database to take the step from Types in Standard modules (this post) to Classes (next post), and see the difference.
I "know" even about classes/OCXs: what they are and how are used but, please, continue what you started: the tutorial. I'm sure that this will give me a better understanding. Thank you again
A Class is like a Type in that it can contain structured data, but inside a Class you actually run code, which is a very interesting capability. With a Type you have to pass a variable of that Type to a Sub or Function to get a result, but a class carries all it's functions around with it, so a class can offer a result immediately, and you don't have to remember how to use it.
The enclosed database opens showing a Customer form, and displays that customer's address. What's important in my mind is how light the code is on the customer form. There is a cCustomer object, and apart from that all the form code ever does is run methods of the classes.
The reason this is awesome is that in general, adding new features in a program with rich classes is easy. You've already solved the problems once that the classes need to perform, and you simply invoke them as required with very little code. Or, if you need to add a feature you add it once, to the class, not to the many forms and/or reports where it might be invoked. And, if I want to open a form to edit a customers address I can run code as simple as . . .
Code:
[COLOR="Green"]'create a new class object[/COLOR]
Dim cus As New cCustomer
[COLOR="Green"]'load CustomerID = 4 and show their address[/COLOR]
cus.Load(4).Address.Show
. . . and all the classes in the chain already know how to do all that stuff. This also makes it way easier to make stuff work with ribbons.
And we haven't even started talking about objects raising their own events . . .
Interesting to note that in this example there is an addressID in the customer record whereas the usual advice would be to have a customerID in the address record.
Like Mihail I haven't used classes much so this is a learning curve for me - so is the above a requirement for using classes in this way or is it just the way you happen to have put the lesson together?
Hoping your answer will be quicker than me experimenting!
No, the address needs to exist independent of any strongly typed object so a Customer might have a BillingAddressID and a ShippingAddressID, but a Contact object needs addresses too, and Suppliers need addresses. It wouldn't make sense to have an address table with a CustomerID, SupplierID, ContactID, and whatever else. Really the pattern we're implementing here is more of a many-to-many, where we could have a CustomerAddress table, like, . . .
tCustomerAddress
CustomerAddressID
CustomerID
AddressID
AddressType (Billing, shipping, and so on)
Like Mihail I haven't used classes much so this is a learning curve for me - so is the above a requirement for using classes in this way or is it just the way you happen to have put the lesson together
I'm not sure what you mean by "the above requirement." This code, including that in the posted database, looks a lot like what I write in a normal database. There are rules the language requires, like, you need to create a new instance using the New keyword . . .
Code:
dim csr as new cCustomer
. . . but exposing classes as properties is a pretty common pattern. Is that what you mean?
@Mark
I actually said "the above a requirement" which you answered in your first paragraph - I was referring to the 'requirement' of having the addressID in the customer record and not having the customerID in the address record which is the way it is usually presented -as a family key.
Thank you all for your comments.
On my side I need time to "compile" the information.
As I can see the thread become more interesting with any new post.