Chapter 2
Understanding Objects
What you will learn in this chapter:
wrox.com code downloads for this chapter
You can find the wrox.com code downloads for this chapter at www.wrox.com/remtitle.cgi?isbn=9781118336922 on the Download Code tab. The code in the Chapter02 folder is individually named as shown in this chapter.
The concepts introduced in this chapter lay the ground work for understanding object-oriented programming (OOP). Before you move on to Chapter 3 you should feel comfortable with the concepts introduced here. You should end this chapter feeling that you understand what objects, properties, and methods are and how they are used in an OOP program. If any of these concepts seem fuzzy after your first reading of this material, read the chapter again.
If any topic presented in this book still seems unclear after two or more passes through the chapter, you should log on to the Wrox website for this book and ask a question about the subject that is giving you trouble. If it's unclear to you, chances are it may be unclear to others. Wrox expends a lot of effort to make that web resource available to you, so use it whenever you feel unclear on some issue or topic.
As you learned in Chapter 1, programming with objects has been around for more than four decades. However, it's only in the last 20 years that object-oriented programming has become the norm rather than the exception. This chapter presents a simple example of how you can use objects in an everyday situation. You then expand on the concepts presented in that example to an actual program you build using Visual Studio .NET and the objects it provides.
Suppose you are the personnel manager for a company and you need to hire someone to fill an important position. After sifting through dozens of résumés, you select one candidate to call to arrange for a face-to-face interview at your company offices. You call her (let's say her name is Jane) on the phone and chat for a few minutes to confirm that she appears to be the right person for the job. You (pretend your name is Jack) make arrangements for Jane to fly to your location, stating that you will meet her at the airport, as shown in Figure 2.1.
However, because the two of you have never met before, you start asking a few questions so that you can recognize each other at the airport. Jane says she's medium height with blonde hair and that she will be wearing a black business suit and carrying a tan leather briefcase. You then describe yourself as 6 feet tall with sandy hair and say that you'll be wearing a gray suit. You then set a date and time for the flight, and everything's ready for the interview.
Perhaps without realizing it, both of you used objects in the course of your conversation. First, without thinking about it, you implicitly created a person class during the phone call. A class is a template used to describe an object. As such, a class is an abstraction, or simplification, of some object you observe in the real world. You can break a class down into two basic components:
The class properties are the data that you want to associate and record with an object. If you want to create a class person object (referred to as clsPerson), a list of properties might include those shown in Table 2.1.
Properties |
name |
height |
hairColor |
eyeColor |
build |
glasses |
clothing |
shoes |
accessories |
gender |
Although not a rule, many programmers use variable names for properties that start with a lowercase letter but use uppercase letters at the start of any words contained within the property name. Because you may want to follow this convention, too, the property names in Table 2.1 follow this convention.
Prior to the phone conversation, the list of properties for the clsPerson named Jane is virtually empty. All you could fill in from her résumé were her name and gender. However, after the phone conversation you filled in almost all the properties for the clsPerson object named Jane. (You might scare her away if you tried to fill in the Build and eyeColor properties over the phone or face a lawsuit.)
While you were filling in a clsPerson object named Jane, she was doing the same thing for a clsPerson object named Jack. Prior to the phone call, the clsPerson object Jane created to be associated with the name Jack may have been totally empty because Jane had no idea who might be calling her about a job interview. However, the dialog on the phone enabled each party to fill in at least some of the property values for the other person. From Jane's point of view, her clsPerson object went from a totally nondescript object to (at least) a partially identifiable object after the phone call was completed.
By changing the values of the class properties, you can change the state of the object. The state of an object is determined by the values of the properties used to describe the object. In the example, the properties used to describe the state of a clsPerson object are those shown in Table 2.1. Prior to the phone call, there are no meaningful values for the properties shown in Table 2.1.
Although people don't change their names often, it happens occasionally. Likewise, people do gain and lose weight, dye their hair, wear tinted contacts, change clothes, and alter their accessories. If any of these property values change, the state of the object also changes. Just keep in mind that anytime the value of a property changes, the state of the object—by definition—also changes.
It should also be obvious that it is the value of the properties that enable someone to distinguish a Jane clsPerson object from a Jack clsPerson object. That is, different objects usually have different values for their properties. It also follows, therefore, that different objects have different states.
Just as there are property values that define the state of an object, there are usually class methods that act on the properties. For a clsPerson object, you would want that object to talk, wave her arms, walk, change clothes, and so forth. In short, the class methods determine the behaviors or actions the object can perform. Methods are used to describe whatever actions you want to associate with the object. Class methods often are used to manipulate the data contained within the object.
You can depict the phone conversation between Jane and Jack as objects of the person class, as shown in Figure 2.2.
Often, class methods are used to take one or more property values, process the data those properties contain, and create a new piece of data as a by-product of the method's process. For example, you might develop a business application and need to create an invoice object that has priceEach and quantityOrdered (among others) as properties of an clsInvoice class. You might then create a method named salesTaxDue() as a class method that computes the sales tax due for the invoice. You might have another clsInvoice property named salesTax that is filled in automatically as part of the code contained in the method named salesTaxDue().
If you think about it, a class property may be viewed as a noun: a person, place, or thing used to describe an object. Class methods, on the other hand, often behave like verbs, denoting some kind of action to be taken on the class properties (that is, the data in the class) or an action the object can perform.
One question that you must grapple with as a programmer is how many properties and methods you should have in a class. For example, in the clsPerson object, you could also include a photograph of the person, fingerprints, a retinal scan, blood type, a DNA sample, dental records, shoe size, plus hundreds of other things that may be part and parcel of a person object. Likewise, you can create methods to simulate talking, running, writing, walking, digestion, elimination, sleeping, dreaming, and a bunch of other actions humans can do. So where do you stop? What is the proper number of properties and methods?
Keep in mind that for every property and method you add to a class, you must write program code to implement that property or method. As a general rule, the less code a program has, the fewer things there are to go wrong. From a programmer's perspective, writing less code is a good thing, provided the code accomplishes the task at hand. In other words, when you design a class, you need to strike a balance between minimizing the code you write and fulfilling the design goals for the class.
The number of properties and methods in a person class the FBI needs to find criminals is going to be vastly different from the number of properties and methods in a person class you might create to write an address book to keep track of your friends.
You'll also find that if you omit unnecessary details in a class, there's a greater likelihood that you can reuse that same class code in some other project. The concept of code reuse is one of the main advantages of object-oriented programming. The more generic the class is, the easier it is to reuse the class. In a sense, therefore, you might want to define your classes as the minimal abstraction necessary to describe an object in a way that fulfills your needs.
Always keep in mind that if there is a simple way and a complex way to accomplish the same goal, simple is almost always the best choice. Some programmers get a kick out of writing clever code that no one else can understand. That's probably okay, as long as no one else has to work with their code. In a commercial setting, however, clever, obfuscated code is rarely a good thing. Given alternatives, stick with the code that is easily understood.
As mentioned earlier, a class is a template for an object. In that sense, a class is like a cookie cutter that enables you to shape specific cookie objects. By this release of Visual Studio, Microsoft has buried within the .NET Framework approximately 4,000 available classes. This means you have approximately 4,000 cookie cutters already hanging on the wall ready for you to use. (You'll use some of these cookie cutters later in this chapter.) Part of your job as a fledgling programmer is to learn about those classes that already exist. After all, there's no reason for you to reinvent the wheel.
There will be times when you will use one of these 4,000 objects but will say to yourself: “If only this object had such-and-such property or had a method that could do such-and-such.” OOP provides a mechanism called inheritance to do just that. However, that topic is deferred to Chapter 16. For the moment, you should concentrate on using those objects built into Visual Studio.
Most of the time you must write some of your own classes in addition to those provided for you by Visual Studio. This means that, after you finish writing your class, you now have 4,001 cookie cutters hanging on the wall, each one of which can create an object of that class's type. But note: Just because you have defined a class (or a cookie cutter) does not necessarily mean you have an object (or a cookie). Just because the cookie cutter your Mom gave you is sitting in the kitchen drawer, in and of itself, doesn't put any cookies on the table.
As stated before, a class is just a template for the object. Just as a cookie cutter works to cut specific shapes out of cookie dough, a class is used to carve out chunks of memory used to hold objects of that class. Until you've used the class template to actually carve out an object, that object does not yet exist.
Figure 2.3 shows the relationship between a class and the objects you can instantiate from that class. The class is shown as a diamond-shaped cookie cutter. The class is named clsDiamond. You can think of the properties of clsDiamond as holding the values that determine the exact shape of the cookie cutter. In other words, the state of the properties makes the class look like a diamond rather than some other shape (for example, a heart). To get an object of the class, you must press the cookie cutter (class template) into the cookie dough (computer memory) to get an actual cookie (object).
An object is also called an instance of a class. The act to use a class to define an object is called instantiation.
An object, therefore, is something that you can actually use in a program. Just as you can't eat a cookie cutter, you can't directly use a class. You must use the class to instantiate an object of the class in memory before you can use it in your program.
Consider the following statement:
clsPerson myFriend;
To help explain what is taking place with this statement, consider the simplified memory map of your computer system, as shown in Figure 2.4.
Simply stated, the program statement statement says: “Go to the cookie cutter wall and look for a cookie cutter named clsPerson. When you find it, take it down from the wall, carve out a chunk of memory for it and label that chunk myFriend.” The end result is that .NET asks the Windows operating system for 4 bytes of memory where it can store a reference, or memory address, for the variable named myFriend.
Assume that the Windows Memory Manager finds 4 bytes of free storage at memory address 800,000. For the moment, you haven't done anything with the memory at this address, so its current value is null. (The word null means the variable has nothing useful currently stored in it.)
What you have at this point is a cookie cutter named myFriend just waiting around at memory address 800,000 for something useful to happen. Things start to happen with the next program statement:
myFriend = new clsPerson();
Words that have special meaning in a programming language are called keywords. The new keyword in C# means that you want to set aside enough memory to hold a new clsPerson object. Further assume that it takes 2,000 bytes of memory to hold all the data associated with a clsPerson object. (.NET figures out how many bytes it needs for an object by examining the program code of the class.) If you verbalize what is happening on the right side of this programming statement, you might say, “Hey, Windows! It's me…Visual Studio. My programmer wants to create a new clsPerson object. Do you have 2,000 bytes of free memory available for such an object?” The Windows Memory Manager then looks through its table of available memory and probably finds 2,000 bytes of free memory somewhere in the system. Assume the 2,000-byte memory block starts at address 900,000. The Windows Memory Manager then sends a message back to Visual Studio and says, “Hey, VS! It's me…the Windows Memory Manager. I found 2,000 bytes of free memory starting at address 900,000.” Visual Studio says, “Thanks,” and proceeds to set up things to use the clsPerson object named myFriend, which now exists at memory location 900,000.
When Visual studio finishes with the statement, the memory map now looks like the one shown in Figure 2.5.
Notice what happened. The value associated with the variable myFriend at memory address 800,000 has changed from null to the memory address where the data associated with the newly created myFriend object is stored. You have now carved out a clsPerson object that you can access via the variable named myFriend. The purpose of the myFriend variable is to tell you where to find the data associated with the myFriend object.
Again, the correct programming term is that you have instantiated a clsPerson object referenced by the variable myFriend. An instantiated object is an object that you can use in your program. It should also be clear that myFriend is a reference that enables you to “find” the object's data (that is, the values of its properties).
Repeating the two programming statements (Chapter 4 explains the purpose of the slashes in the following statements):
clsPerson myFriend; // Grab a specific cookie cutter from the wall // and name it myFriend myFriend = new clsPerson();// Use the cookie cutter to make a clsPerson // cookie and refer to it as myFriend
The first statement means that you intend to create an object of clsPerson and refer to that object using the variable named myFriend. The second statement says you carried through with your intent and instantiated a new object of a clsPerson type and associated it with the object variable named myFriend. You reference the object's data for the remainder of the program using the variable named myFriend. Using the cookie cutter analogy, the cookie cutter (that is, the reference) is found at memory address 800,000, but the cookie itself (that is, the object's data) is found at memory address 900,000.
You can combine the two statements into a single statement if you want:
clsPerson myFriend = new clsPerson();
The interpretation is exactly as before. You have simply collapsed the two statements into one. Because programmers prefer less code to more code, you will see the abbreviated form used more often.
You must understand the difference between a class and an object of the class. When you write the code for a class, you are actually acting like an architect designing a house. The plans you craft might be used to build one house or hundreds of houses. The point is that the class code is the design to be used at some later date. Later, carpenters, plumbers, and other craftsmen actually construct the house. The craftsmen grab the blueprints (that is, the class) from the wall, read them, and then instantiate the house. The “usefulness” of a class (that is, a house) actually comes into being after an object of the class is instantiated (that is, the house is actually built). Although Santa indirectly appreciates what a cookie cutter does, he'd much rather see a pile of (instantiated) cookies on a plate than a stack of rusting cookie cutters.
You now have instantiated an object of the clsPerson type. The obvious question is, “So what?” At the present time, the object named myFriend is fairly nondescript. Indeed, every property in the object is instantiated with the value of zero, false, or null by default, depending upon the data type of the property. None of the information previously presented in Table 2.1 has been filled in for the myFriend object. You need to change the faceless myFriend object into one that has some meaningful information in it.
You can change the object by changing the information contained in the properties defined within it. From Table 2.1, you might do the following:
myFriend.name = “Jane”; myFriend.gender = “F”; myFriend.height = 66; myFriend.build = “Petite”; myFriend.hairColor = “Blonde”; myFriend.eyeColor = “Blue”; myFriend.clothing = “Business casual”; myFriend.accessories = “Tan leather briefcase”;
Notice what these statements do. They change the faceless myFriend object into one that gives you some impression of what this particular clsPerson object looks like. In these statements, the equals sign (=) is called the assignment operator. The assignment operator takes the information in the expression on the right side of the assignment operator and copies that information into the expression on the left side of the assignment operator. This means that somewhere near memory address 900,000, the name property has been changed from null to Jane. Similar changes have occurred for the other myFriend properties. In programming terms, the assignment statements change the state of the object from a faceless, nondescript person to an attractive female named Jane.
It is important to note the syntax associated with changing the state of an object. The syntax of a programming language refers to the rules governing the use of the language. The general syntax for using the property of an object is as follows:
objectName.Property
Note the period, or dot, between objectName and Property. The period that appears between the object name and the property (or method) name is called the dot operator in C#. If you think of an object as a steel box surrounding the properties and methods of the object, the dot operator is the key that unlocks the object's door and lets you inside the object to gain access to the object's properties and methods.
In the statement
myFriend.Name = “Jane”;
the computer processes the statement from right to left. You might verbalize the process implied by the statement as the following sequence of steps:
You can visualize these steps, as shown in Figure 2.6.
Now that you've followed these steps, the object associated with the myFriend variable has a name assigned to it. This also means that the state of the myFriend object has changed from an object with no name to one with the name Jane.
The object may be used “in the opposite direction,” too. For example, suppose you have assigned Jane's name to the myFriend object. Later in the program you want to display her name in a textbox object on a form. If the textbox object is named txtName, you could write
txtName.Text = myFriend.Name;
Again, because an assignment statement is processed from right to left, the code causes the program to do the following:
You can visualize these steps, as shown in Figure 2.7.
This sequence of steps illustrates two important concepts to remember:
You can make two important generalizations from these observations:
Although there are a few exceptions, these generalizations can serve you well most of the time.
In an oversimplified way, the discussion in this section describes how objects work and how properties, or the object's data, should be hidden inside an object. Why hide the properties inside an object? You hide them for the same reason that kings used to hide their daughters in the castle tower…to keep other people from messing around with them.
By encasing the properties (data) of the object inside a steel box, you can restrict access to those properties by forcing everyone to have a proper key (the object's dot operator) to gain access to the properties. Stated differently, you bury the data inside an object in an attempt to protect the data from accidental changes by other parts of the program. The process to hide data within an object is called encapsulation. Encapsulation is one of the cornerstones of OOP. You learn more about encapsulation and the other elements of OOP in later chapters. For the moment, however, just keep in mind that you want to encapsulate (hide) your objects' data as much as possible, yet still make that data available to those parts of your program that need it.
In the following Try It Out you write a simple program that uses a few of the many objects that Visual Studio provides for you. Follow the same general directions you used to create the program to test your installation of Visual Studio in Chapter 1.
Listing 2-1: The C# frmMain Template Code
using System; using System.Windows.Forms; public class frmMain:Form { #region Windows Code private void InitializeComponent() { } #endregion public frmMain() { InitializeComponent(); } [STAThread] public static void Main() { frmMain main = new frmMain(); Application.Run(main); } }
this.label1 = new System.Windows.Forms.Label();
clsPerson myFriend = new clsPerson();
this.label1.Text = “Name:”;
private void button1_Click(object sender, EventArgs e) { }
frmMain.ActiveForm.Text = textBox1.Text;
Experimenting like this is a great way to see how changes to the various properties affect the program. If you're like most beginning programmers, you might be a little afraid that you'll “break” something. Not to worry. You might get an ugly looking form or something else weird may happen, but it's nothing you can't fix by simply resetting the property to its original value. For that reason, it's best if you make changes to a property “one at a time” so that you are sure of that change's impact, plus being able to return the property to its original state if you want to do so. Please…experiment and have fun with this simple program.
Although you did just write a complete Windows program, it's a simple program that was kind of thrown together off the top of my head. In real life, programming tasks are rarely this simple.
Now put what you've learned thus far to use designing a program. Of necessity, you can leave out some code that would make your little program more bulletproof simply because you haven't learned how to do that yet. (You will learn how to make programs more bulletproof soon enough.) The goal here is to show you how to start writing a program to solve a specific task.
Without a doubt, one of the hardest things for beginning programmers to figure out is where to begin writing a program. As a general rule, when I assign an in-class programming assignment, most students start solving the program problem by dragging and dropping controls from the toolbox onto a form as their first step in writing a program.
Wrong!
Don't confuse movement with problem solving. If you start dragging and dropping objects all over the place without a coherent plan, you're in for some unexpected disappointments. Inevitably, you'll end up backtracking and developing a plan of attack for the task at hand, so you may as well start out with a plan in the first place.
Indeed, to write good programs you must have a plan. Central to a programming plan is an algorithm. An algorithm is simply a step-by-step recipe, or plan, for how you want to solve the programming task at hand. A good place to start a program plan is with the Five Program Steps.
As a broad generalization, you can describe all programming problems in terms of five steps.
The Initialization step involves those things that are done before the user sees anything displayed on the screen. For example, some programs “remember” the last four or five files that you worked on with the program and tack those file names on to the end of the File menu. Microsoft Word, Excel, PowerPoint, Visual Studio, and other programs do this in one way or another. Clearly, those programs must read that information from somewhere before the program can display the File menu on the screen.
Likewise, a program might establish a network connection, initialize a printer, connect to a database, read a sensor, or do all kinds of other activities before the user sees anything on the screen. All these activities are lumped under the Initialization step.
Simply stated:
The Initialization step is responsible for establishing the environment in which the program is to be run.
The activities of the Initialization step take place before the user sees anything appear on the screen.
If you think about it, all programs take some kind of input into the program, process it in some way, and then display some variation of the data that was derived from the original inputs. The Input step, therefore, is concerned with getting those inputs into the program.
Most of the time you tend to think of program input as coming from the keyboard or mouse. However, input can come from many other input sources, too. Some examples are an engine sensor, bar-code reader, retinal scanner, network connection, fire or smoke alarm sensor, information from a database, or a host of other input devices. The important aspect of the Input step, however, is that data in some form is collected by the program.
It's always a good practice to validate the input data. Users do make mistakes when entering data, network connections sometimes do weird things, data sources can be locked, and other unexpected occurrences can corrupt the data. You will learn more about data validation in later chapters. For now, I have assumed a really bright user who never makes a typing mistake and all the hardware is working perfectly! (This assumption enables you to concentrate on the essence of the objects being used in the program.)
This is the step in which you take the program inputs and process them in some way. You might perform some kind of mathematical operation on them, or perhaps the inputs are the name and account number of a customer, and this step reads a database to calculate a bill to be sent to the customer. In a jet aircraft, the inputs might be the current airspeed and GPS coordinates to calculate when the plane will touch down at a specific airport. In all cases, the Process step involves taking the inputs, acting upon them, and producing some form of result or answer.
What is meant by “taking the inputs and acting upon them”? That sentence fragment should make something itch in the back of your mind. What do you call those thingies that take an object's data and act upon them? Right…methods! Quite often, therefore, the Process step uses class methods to transform the data provided by the Input step. Hmmm? Could it be that the data used in the Process step might be provided by the properties of the class?
You must remember that often a variety of methods can be used to take the same inputs, process them, and produce the same answer. Just as you can find a variety of ways to fly from New York to Los Angeles (such as nonstop, or through Chicago, or Denver, or Houston), the result's the same: You end up in L.A. Likewise, you can use different algorithms to take the same inputs into a program, process them differently, but generate the same results. For example, there are dozens of different algorithms available to sort data using a computer. However, as you shall see in subsequent chapters, some algorithms are more efficient than others. Always keep your mind open to new ways to accomplish old tasks.
The Display step is responsible for displaying the results produced in the Process step. Usually, this means showing the results on the display screen, but other options exist. Perhaps it means skipping the display screen and simply printing a bill and mailing it to a customer. Or perhaps the program is responsible for updating a customer record in a database. There are many programs that run themselves (called batch programs) and don't display anything on the screen or ask for any inputs from the user as they run. Instead, batch programs often start automatically at a predetermined time (perhaps at 1 a.m. when no one else is using the system), read the input information from some input source (like a database), and generate reports based on the input information.
The Display step doesn't always result in your “seeing” a result displayed on a screen. Sometimes the result of the program is passed on to another process, or a database, a website, or even automatically sending an email. The key is that you have used the inputs to produce something new and you want to “see,” “use,” or “save” that new result. However, for most of the programs you write while using this book, the Display step does show some information to the user via the display device.
The Termination step doesn't necessarily mean that you simply end the program. It also means “cleaning up” after the program has finished. For example, if the Initialization step reads the four latest files used in the program and appends them to the File menu, then this step needs to update those filenames to reflect what the user has done during the current session of the program. If the Initialization step sets up a printer or database connection, this step should close that connection.
Quite often, whatever is done in the Initialization step is “undone” in the Termination step. Finally, the Termination step should provide a graceful exit from the program, even if an error occurred while the program was running. (You learn more about how to plan for error conditions in Chapter 11.)
Now use the Five Program Steps you just learned to design and write a simple program. The goal of the program is to gather address information about the user and then simply redisplay that information as if it were a mailing label. For now you won't actually do anything with the information, but after you get a little more C# programming under your belt, you could use this program as an input process for some larger programming task, such as creating a customer account or writing your own personal mailing list. The primary objective here, however, is to get you additional experience using a few of the objects provided to you as part of Visual Studio and .NET.
Your first task is to develop a plan to solving the task at hand. A good place to start the plan is with the Five Program Steps presented earlier.
This is a simple program, and you don't actually have any fancy initialization to do. Any necessary initialization tasks are done automatically for you by Visual Studio. You take a look at Visual Studio's background handiwork a little later.
This is the step where you must ask yourself, “What information do I need to solve the programming task at hand?” Because you want to arrange user information the way a mailing label does, you need to know the user's:
You could write the program and hard-code values in for each piece of data into the program. When you hard-code a value into a program, it means that you make that value a permanent part of the program itself. If you hard-code the data for this program, you write program code that embeds the user's name and address as data directly into the program. However, hard-coding values directly into a program makes the program less flexible than it might otherwise be. For example, each time you wanted a mailing label for a new user, you'd need to write new data values into the program's code before you could display the information. Not good.
A more flexible approach is to ask the user to enter the necessary information while the program runs and to display the information as a mailing label when they finish. After they enter the information, you can arrange the data for display on the screen. The easiest way to code this approach to the solution is to use label and textbox objects for each piece of information, or input, you want to collect. When the user is satisfied that he has entered the requested information, he can click a button to display the information as a mailing label.
In this program, you decide to use labels and textboxes to collect the information from the user. The labels help to inform the user about the data you are requesting, and the textboxes provide a means for him to type in his responses. These labels and textboxes become part of the user interface of your program. A program's user interface simply refers to how the program interacts with the user running the program. In this program the user interface consists of labels, textboxes, and buttons arranged on a form.
Entire books have been written about how to design a good user interface. Although this chapter can't possibly do justice to the subject of user interface design, consider the following guidelines:
Always keep in mind that the goal of a user interface is to make it easy for the user to run your program. Try to make the user interface intuitive and simple from the user's point of view.
This is the step where you devise the algorithm that produces the wanted result of the program. The wanted result of this program simply is to display the user information in a mailing-label format. The Input step provides you with the information you need via the user interface you designed. Therefore, to actually begin the Process step, you need to have a button (such as a Display button) so that the program knows when all the input data has been entered and it can begin to address the task at hand.
Microsoft Windows programs are event-driven. An event-driven program means that some agent (such as the user) must generate an event of some type for the program to perform the next program task. For example, in this program, if the user does not interact with the program in some way, the program will obediently sit there and wait until she does. In your program, the user must click the Display button to trigger the event associated with the Process step.
Human action is not the only way to trigger a Windows event. For example, the time of day might be constantly monitored by a program, and when a certain time is reached, the program triggers an event that causes that program to perform some type of action. A fire sensor might sit there for years monitoring the fire sensors and never trigger an event. But when the fire sensor does trigger an event, alarms go off, sprinklers turn on, and automated phone calls are sent out. For most of the programs you will write in this book, however, the user provides the activity (for example, a mouse click or a keystroke) that fires a Windows event.
The Process step for this program is simply to arrange the information entered by the user in the form of an address label. A button-click event triggers the Process step and is used to inform the program that all the necessary data has been entered and is ready for use. (Assume the user does not trigger the event before all the data is entered. This is a bright user, remember?)
In this program, you simply display the user's name and address information as a mailing label.
Because you didn't write any code to do anything tricky in the Initialization step, you don't need to do anything in the Termination step to clean up and gracefully end the program. Instead, you can simply have a button object (such as an Exit button) that the user can click to end the program.
Now you have a plan based upon the Five Program Steps. Planning does not require you to do any programming. However, skipping this planning process is a common mistake all programmers make, including seasoned programmers.
Still, many of you will be like most of my students: You'll plunge ahead without formulating any kind of plan…and that's okay. However, eventually, you have to formulate a program plan, and it's always quicker to do it at the outset.
Now, all you need to do is implement your plan.
Now that you have a plan, you need to write the program code to solve the task at hand. You're going to do this by using the objects provided by Visual Studio and .NET in concert with your own program logic using C#. It won't be difficult for you to do this because…you have a plan! Because you started with a plan rather than by just dragging and dropping objects onto a form puts you light years ahead of most beginning programmers.
True, you too will be dragging and dropping objects onto a form soon enough, but your approach to the problem is different. There's a method to your dragging and dropping…it is guided by “the plan.” Too often beginning programmers start dragging labels and textboxes onto a form without a plan. However, movement without a plan generates more heat than light. Therefore, the lesson to learn is:
Never start coding a program without a plan.
Although you performed in Chapter 1 some of the steps presented in the next few sections, you didn't learn why you were doing those steps. Also, you do things a little differently now that you have some understanding of OOP. In the following Try It Out you begin.
Object Name | Property Name | Property Value |
frmMain | Text | Mailing Label Program |
StartPosition | CenterScreen | |
label1 | Text | Name: |
AutoSize | False | |
BorderStyle | Fixed3D | |
Size | 75, 20 | |
label2 | Text | Address: |
AutoSize | False | |
BorderStyle | Fixed3D | |
Size | 75, 20 | |
label3 | Text | City |
AutoSize | False | |
BorderStyle | Fixed3D | |
Size | 75, 20 | |
label4 | Text | State |
AutoSize | False | |
BorderStyle | Fixed3D | |
Size | 40, 20 | |
label5 | Text | ZIP |
AutoSize | False | |
BorderStyle | Fixed3D | |
Size | 40, 20 | |
txtName | Default values for all properties | |
txtAddress | Default values for all properties | |
txtCity | Default values for all properties | |
txtState | Default values for all properties | |
txtZip | Default values for all properties | |
txtDisplayOutput | MultiLine | True |
ReadOnly | True | |
btnDisplayOutput | Text | &Display |
btnExit | Text | E&xit |
private void btnDisplayOutput_Click(object sender, EventArgs e) { }
private void btnDisplayOutput_Click(object sender, EventArgs e) { String buffer; buffer = “Mailing Label:” + Environment.NewLine + Environment.NewLine; buffer = buffer + “ Name: “ + txtName.Text + Environment.NewLine; buffer = buffer + “Address: “ + txtAddress.Text + Environment.NewLine; buffer = buffer + “ City: “ + txtCity.Text + “ State: “ + txtState.Text +” Zip: “ + txtZip.Text; txtDisplayOutput.Text = buffer; }
private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.txtName = new System.Windows.Forms.TextBox(); this.txtAddress = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.txtCity = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.txtZip = new System.Windows.Forms.TextBox(); this.label5 = new System.Windows.Forms.Label(); this.btnDisplayOutput = new System.Windows.Forms.Button(); this.btnExit = new System.Windows.Forms.Button(); this.txtDisplayOutput = new System.Windows.Forms.TextBox(); this.txtState = new System.Windows.Forms.TextBox(); this.SuspendLayout(); // // label1 // this.label1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.label1.Location = new System.Drawing.Point(12, 18); this.label1.Name = “label1”; this.label1.Size = new System.Drawing.Size(75, 20); this.label1.TabIndex = 0; this.label1.Text = “Name:”; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // txtName // this.txtName.Location = new System.Drawing.Point(93, 18); this.txtName.Name = “txtName”; this.txtName.Size = new System.Drawing.Size(297, 20); this.txtName.TabIndex = 1; // More statements follow } // End of the InitializeComponent() method
this.label1 = new System.Windows.Forms.Label();
this.label1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.label1.Location = new System.Drawing.Point(12, 18); this.label1.Name = “label1”; this.label1.Size = new System.Drawing.Size(75, 20); this.label1.TabIndex = 0; this.label1.Text = “Name:”; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
String buffer;
buffer = “Mailing Label:” + Environment.NewLine + Environment.NewLine;
buffer = buffer + “ Name: “ + txtName.Text + Environment.NewLine;
Mailing Label: Jane Holcar
buffer = buffer + “ Name: “ + txtName.Text + Environment.NewLine; (5) (1) (2) (3) (4)
txtDisplayOutput.Text = buffer;
The program code found in the btnDisplayOutput click event is an example of what I call RDC…Really Dumb Code. This is true for a number of reasons. First, there is no validation of the contents that have been typed into the various textboxes by the user. To make the program code smarter, you should add code to verify that something was typed into the textbox and that the data is appropriate for the information wanted. For example, if you want to validate the content of the Text property for the ZIP code textbox object, perhaps you should check to see if only digit characters are entered. Or if there is a mixture of alpha and digit characters, perhaps you should check to make sure exactly six characters were entered. This way you could allow Canadian postal codes, too, because those codes use both alpha and digit characters.
A second improvement would make the output a little easier to read. If you look closely at the output in Figure 2.16, you can notice that the fields don't line up properly. This is true even though you made an effort to align the Name, Address, and City fields in the program statements by using an appropriate number of spaces before each field. Why didn't this work?
The reason is that label objects have their default Font property set to the Microsoft Sans Serif font. This is what is called a TrueType font. A TrueType font is a variable-spaced font. What that means is that each character in a TrueType variable font takes up only as much screen space as it needs to be seen on the display device. The letter I, for example, takes up less space on the screen than does the letter W. Therefore, because different letters take up different amounts of space, it is difficult to get letters on different lines to align with each other.
You can solve this alignment problem by changing the font to a fixed font. With a fixed font, each letter takes up exactly the same amount of space. If you change the Font property of the txtDisplayOutput object to the Courier New font, which is a fixed font, the letters align perfectly. You can see the result of the font change by comparing the display output in Figure 2.16 with that in Figure 2.17.
You covered quite a bit of ground in this chapter. The remainder of this book builds upon the basic concepts presented in this chapter. You should spend enough time with the material presented here to be comfortable with all the terms introduced in this chapter (such as class, object, property, method, dot operator, instantiate, and so on). A little time spent now learning these concepts can generate huge rewards in later chapters. Take your time, experiment, and have fun.
You can find the answers to the following exercises in Appendix A.
clsThingie myThingie; myThingie = new clsThingie();
TOPIC | KEY POINTS |
Class | A simplification of something |
Property | A piece of data that is used to describe an object and its state |
Method | An action, or ability, you want the object capable of doing |
Object | A piece of memory that implements the design of the class. If a class is the architectural drawings of a house, object is the house. |
Dot operator (.) | Used to gain access to an object's properties or methods |
User interface | How the end user interacts with a program |
Five Program Steps | A methodology for designing a program |
3.149.249.154