DevCity.NET - http://devcity.net
Objects and the ListBox
http://devcity.net/Articles/373/1/article.aspx
Larry Blake
Larry has been a professional developer since the 1980s, on a broad range of platforms. An early adopter of PC technology, he started fooling around with Visual Basic 1.0 in 1992. Today he is Microsoft MVP in Visual Basic. Larry is available for contract coding in VB, C# and ASP, and for Project Management. See: http://mysite.verizon.net/lmmblake . 
by Larry Blake
Published on 6/5/2009
 

In this article, I introduce some Object-Oriented Programming concepts while creating a Custom Object.  I then fill a ListBox with the Custom Objects, and demonstrate some interesting features of the ListBox.


Custom Objects

 

Custom Objects are the foundation of OOP (Object Oriented Programming). An Object is a logical entity that can be referenced and manipulated using program code. It typically contains both a data portion and executable methods. For example, consider this fragment:

TextBox1.Text = "Hello, world!"
TextBox1.SelectAll()

The first line changes one of the Object's data properties, and the second line executes one of the Object's associated methods. Actually, to be slightly more precise, the fragment performs this work upon a specific Instance (TextBox1) of the general Object (TextBox). In the Visual Studio editor, IntelliSense helps you with the available operations, and it is smart enough to recognize what operations are appropriate for a particular Instance.

A Custom Object is pretty much what you would expect it to be. Instead of using just the Objects that are built into the .NET Framework, you create your own. You can instantiate (create Instances of) Custom Objects, just as you would with a TextBox or a Button. And IntelliSense will recognize the appropriate properties, methods, etc.  The properties and methods comprise a standard interface to the Object. 

It's possible to write some pretty powerful programs in Visual Studio without using Custom Objects. So... why do you need them? The main justifications are reusability and centralized maintenance. If you have a small Object that performs a limited number of tasks, you can reference it in many places and expect it to behave consistently. As you add features to the Object or fix bugs, you don't need to worry that you updated Program A but not Program B (because the code is in the Object, not in A and B). Disentangling the object logic from the rest of the program makes for shorter, cleaner code. In OOP terminology, communicating via an interface is called Abstraction, and hiding the program details is called Encapsulation.

Returning to the TextBox example, you wouldn't want to fill your program with complex Windows logic every time you display a TextBox. Abstraction simplifies your life by insuring that you deal with an Object conceptually. Encapsulation allows Microsoft to make underlying changes so that your program can run on Windows 2000, XP, Vista, Windows 7, new hardware, etc. without affecting you. They could change the underlying programming language altogether, and as long as the interface is compatible, you wouldn't care. Think of Encapsulation as a "Black Box" that you don't need to open.

The Person Object

Okay, let's create a Custom Object! Go into Visual Studio and choose "Create Project" and use the template Windows Forms Application. It will show you an empty Form1, which you should ignore for now. Go to "Project | Add Class" and use the template "Class". Name it "Person.vb", and press Add. Change the code to:

Public Class Person
   Public FirstName, LastName As String
End Class

Well, there you have a very simple Custom Object. Now go back to your Form1 and double-click the form's surface. As you enter the following code, you'll notice that IntelliSense is aware of the appropriate choices.

Private Sub Form1_Load(ByVal sender ...
   Dim p As Person
   MessageBox.Show(p.FirstName)
End Sub

Click the "Play" button (green triangle in toolbar, or press F5). You'll get the notorious "Object reference not set to an instance of an object" message. This message means that although p is defined as a Person object, you have not invoked the appropriate initialization code to instantiate it. Make the changes below, and this will become clearer.

Private Sub Form1_Load(ByVal sender ...
  Dim p As Person

  p = New Person()
  p.FirstName = "Joe"
  MessageBox.Show(p.FirstName)

  p = New Person()
  p.LastName = "Smith"
  MessageBox.Show(p.FirstName)
End Sub

This time, there is no error when you press Play. You created a New person instance p, so the first MessageBox shows Joe. The second MessageBox is empty, because you replaced the previous instance p with another new instance, and the new p has only a LastName.


 

Improving the Person Object

 

(Don't you wish it was so easy to improve people?) Lets add a bit more code, and then talk about it.

Public Class Person
  Public FirstName, LastName, Title As String
  Private _salary As Integer

  Public Sub New(ByVal f As String, ByVal l As String)
    FirstName = f
    LastName = l
    Title = "Employee"
    _salary = 50000
  End Sub

  Public Sub New(ByVal f As String, ByVal l As String, ByVal t As String)
    FirstName = f
    LastName = l
    Title = t
    _salary = 50000
  End Sub

  Public Sub New(ByVal f , ByVal l As String, ByVal t As String, ByVal s As Integer)
    FirstName = f
    LastName = l
    Title = t
    _salary = s
  End Sub

  Public Sub GiveRaise()
    _salary *= 1.03
  End Sub

  Public ReadOnly Property Salary()
    Get
      Return _salary
    End Get
  End Property

  Public ReadOnly Property Info()
    Get
      Dim s As String = FirstName & " " & LastName & vbCrLf _
        & "Title: " & Title & vbCrLf _
         & "Salary: " & _salary.ToString()
      Return s
    End Get
  End Property
End Class

The first thing I want to point out in this code is the use of the words Public and Private. Think of Form1 as a user of the Person object. Public variables, methods, properties, etc. are usable in Form1 and visible to IntelliSense. Private ones are hidden. (Again, showing a small, concise interface is Abstraction.  The unnecessary details are hidden by Encapsulation.)

The private variable _salary is used in the public property Salary. A Property is similar to a regular variable, but it can be made read-only as I've done here. So in Form1, the first of the following statements would be valid.  The second would cause an error.

MessageBox.Show(p.Salary.ToString())
p.Salary = 60000 ' ERROR! 

Properties can also have more complex code, as for example in Info().


The improved Person code has several subroutines called New(), which introduces two concepts. First, New() is a special type of method called a Constructor. A Constructor is the code that executes when the object is instantiated.


The second concept is Overloading. Overloading is the reuse of a method name with a different set of parameters. You probably already use overloaded methods without thinking about it. Heres one example.

MessageBox.Show(p.FirstName)
MessageBox.Show(p.FirstName, "My name is")

A set of parameters is called a Signature. Signatures must vary in total count and/or variable type. (Changing just a variable name is not a new signature.) Again, IntelliSense is aware of the different signatures and will suggest possible syntax variations.

 

 

Creating a UI

 

Okay, now let's use our Custom Object in a Windows form. Go back to Form1 in design mode and add two buttons and a ListBox. Name the buttons "btnInfo" and "btnRaise". The form should look like this:

Now change the Form1_Load event as follows:

Private Sub Form1_Load(ByVal sender ...
   Dim p As Person

   With Me.ListBox1
      .Items.Clear()

      p = New Person("Joe", "Smith")
      .Items.Add(p)

      p = New Person("Mary", ""Dellacorte", ""Manager")
      .Items.Add(p)

      p = New Person(Hubert", "King", "President", 100000)
      .Items.Add(p)

      p = New Person("Ronald", "King", "Boss' Nephew", 80000)
      .Items.Add(p)

      .SelectedIndex = 0
   End With
End Sub

This new version creates Person objects and loads them into a ListBox on the form. The person variable p is re-used. Notice the use of different Constructors.

If you press "Play", you'll see that adding an Object to a ListBox doesn't give you the same result as adding a String would. The ListBox contains four copies of something like "MyProjectName.Person".  So let's add one more bit of code to Person.

Public Overrides Function ToString() As String
   Return FirstName & " " & LastName
End Function

The ListBox can't directly display an Object, so it tries to convert the Object to a String before displaying it. All Objects have a ToString() function, and the "MyProjectName.Person" came from the default implementation. By using Overrides, we give it a new behavior.

Overriding behavior is part of another OOP concept, Inheritance. Our Person object inherits behavior from the "parent" class Object.  Person could be the "parent" to new "child" objects as well.  Children can use the logic of their parent objects (and grandparent objects, etc.), "as is" or with overrides.

When we press "Play", the ListBox now contains the correct Person names.

 

Almost Finished

The interesting thing about ListBoxes is that although they display Strings, they contain Objects. You can see the difference when we add the final code to implement the buttons. From design mode in Form1, double-click the buttons to enter their code.  Here is how it should end up:

Private Sub btnInfo_Click(ByVal sender ...
   Dim p As Person

   p = Me.ListBox1.SelectedItem
   MessageBox.Show(p.Info)
End Sub


Private Sub btnRaise_Click(ByVal sender ...
   Dim p As Person

   p = Me.ListBox1.SelectedItem
   p.GiveRaise()
End Sub

Now move the selection bar in the ListBox up and down, and press the "Info" button. You'll get something like this:

Clearly, the ListBox is keeping track of more than just Strings. But that's not all. These are fully functioning objects. Press the "Raise" button, then press "Info" again.  You'll see that the salary has been raised.  You can add multiple raises or select a different Person, and the ListBox will continue to maintain each Person separately.

This feature makes the ListBox good as a temporary data store.  Another common use is to display a name while remembering an associated number (course number, employee number, product number, etc.).

In this example, the button code takes the selected line in the ListBox and moves it into a usable Person variable. You can easily replace Person with an other object type to suit your purposes.

 

Summary

We have looked at a number of topics quickly.  To recap:

  • Creating Custom Objects is your first step towards becoming an OO programmer.
  • Abstraction and Encapsulation encourage smaller, more reusable code.
  • Public properties (and methods, etc.) implement Abstraction.
  • You can create Instances of Custom Objects just as you can for Toolbox Objects.
  • IntelliSense works on Custom Objects.
  • Use the Constructor method New() to execute any initialization code.
  • Overloading is using the same method name with a different Signature.
  • Use Overrides to change default behavior on Inherited objects.
  • The ListBox contains Objects, but displays Strings. 

I hope this was helpful.

LB