To continue our code, right-click on the project in Solution Explorer and select Add and then Class.... Name the class SigBlock.
The SigBlock object starts out with a VB.NET elements we haven't seen before - Structures:
Public Class SigBlock Private _SigBlock As New SigBlockStruct Private Structure SigBlockStruct Dim FName As String Dim LName As String Dim Address As String Dim Phone As String Dim SSN As String Dim Email As String End Structure End Class
We're using this structure as a way of grouping other data types under a single name. The first statement creates a new instance of the structure. When we use the elements in the structure, we'll have to qualify them with the name of this new instance. The member variables are initialized in the New subroutine. This is called a "constructor". It's executed automatically when a new instance of the object is created.The structure only holds the information that is used by other parts of the program. One that hasn't been coded yet is called SigFieldDisplay, but let's look at what the code in the structure looks like anyway. (If you enter this code into the SigBlock Class, you'll see an error because the SigFieldDisplay object doesn't exist yet. Here's our New subroutine:
Public Sub New() _SigBlock.FName = "Dan" SigFieldDisplay.FName.Checked = True _SigBlock.LName = "Mabbutt" SigFieldDisplay.LName.Checked = True _SigBlock.Address = "About Visual Basic" SigFieldDisplay.Address.Checked = True _SigBlock.Phone = "(555) 555-5555" SigFieldDisplay.Phone.Checked = False _SigBlock.SSN = "123-45-6789" SigFieldDisplay.SSN.Checked = False _SigBlock.Email = "email@example.com" SigFieldDisplay.Email.Checked = True End Sub
Because the New subroutine includes statements that directly access another object like this ...
SigFieldDisplay.FName.Checked = True
... we know that something is wrong with our design. The OOP principle of encapsulation states that the object should be completely self contained, yet this statement references a component somewhere else in the program.
Initializing variables in the New constructor to provide default values will work as long as we're only managing the information for one person. As soon as it has to manage two or more, we'll need to use a data store such as a file. And there's another serious design flaw that will have to wait until we start using a file. Although we can change what is displayed, we can't save any of our changes!
The Lines ReadOnly Property
Since the function of the SigBlock is to maintain the information used by other parts of the system, we can code a ReadOnly Property to pass it back to another object in exactly the form we want by coding a custom getter in the Property. I named the property Lines. There's quite a lot of repetitive code in it but the general form is like this:
ReadOnly Property Lines() As String Get Lines = vbNewLine If SigFieldDisplay.FName.Checked Then Lines &= _SigBlock.FName & " " End If If SigFieldDisplay.LName.Checked Then < ... and so forth ... > End Get End Property
The Lines Property is accessed just like other properties (for instance, the Text property of a Button or TextBox):
SigBlockBox.Text = mySigBlock.Lines
To create the Lines Property in the getter, I start out by assigning vbNewLine to the Property. This is an example of an intrinsic constant. These are values that are often hard to remember but are often used in program statements. VB.NET makes life a little easier on you by giving them a name you can remember and then making them a built-in part of the language. In this case, vbNewLine is actually equal to a string combining two unprintable characters - Chr(13) + Chr(10) or "Carriage Return and Line Feed". This value gives you a new line in a display. You can read up on them by searching Microsoft's MSDN site for "Intrinsic Constants and Enumerations".
I add a new value to the end of the string by using the &= short circuit operator. This is just a syntax trick that C programmers have used for a long time. The only real value is to save a few keystrokes. The statement in the program is equivalent to:
Lines = Lines & _SigBlock.FName & " "
Again, the encapsulation principle isn't followed (If SigFieldDisplay.FName.Checked). But this statement does have another lesson. Note that since the Checked property is a Boolean (True or False) value, we can test it directly instead of coding, "If SigFieldDisplay.FName.Checked = True".
To change the values that are displayed in the signature block, a new form is displayed with ...
Since these are the values that are tested in the SigBlock object, we don't have to do anything else.
SigFieldDisplay is a secondary form. Add it by right-clicking the project in a way similar to the way we created a class earlier. It's simply a form with six checkboxes and a button. The button hides the form again. This makes the form, in effect, a temporary datastore to save our display selections ... at least until the program ends. You can see the code for this secondary form in the download.