Dr. GUI .NET 1.1 #3

 

June 17, 2003

Summary: Dr. GUI shows the canonical polymorphism example, an application that derives drawable objects from a common abstract base class. He also demonstrates using an interface, and a few design patterns to factor out common code when implementing an interface or abstract base class. Finally, he demonstrates a simple Windows Forms application and an ASP.NET version of the drawing application that manipulates a bitmap on the server side, without assistance from client-side script or other client code. (42 printed pages)

Links

Dr. GUI .NET home page
Source code for this article
Run the sample GraphicalASPX application.

Contents

Introduction
Where We've Been; Where We're Going
Putting It All Together: The Drawing Classes
Using Our Drawable Objects in a Windows Forms Application
Using Our Drawable Objects in an ASP.NET Application
Passing State Between Pages and Requests
Drawing in a Separate Page
Give It a Shot!
Yo! Let's talk!
What We've Done; What's Next

Speak Out!

Tell the world and us what you think about this article on the Dr. GUI .NET message board. Or read what Dr. GUI's thinking—and comment on it—on his blog.

Be sure to check out the sample running as a Microsoft® ASP.NET application. Or just take a look at the source code in a new window.

Introduction

Welcome back to Dr. GUI's fourth Microsoft® .NET Framework programming article. The first three are called—in honor of .NET Framework array element numbering, which starts at zero rather than one—Dr. GUI .NET 1.1 #0 through Dr. GUI .NET 1.1 #2. If you're looking for the earlier articles, check out the Dr. GUI .NET home page.

Again, Dr. GUI hopes to see you participate in the message board. There are already some good discussions there, but they'll be better once you join in! Anyone can read the messages, but you'll need a Microsoft .NET Passport to authenticate you if you want to post. But don't worry—it is easy to set up a Passport and you can associate it with any of your e-mail accounts.

Where We've Been; Where We're Going

Last time, we reviewed inheritance and polymorphism and showed how the .NET Framework implements them.

This time, we're going to see a more complete example of inheritance, abstract (MustInherit) base classes, and interfaces in a cute drawing application. This won't be a console application; because it's graphical, it will be a Microsoft® Windows® Forms application instead. (That'll give us the chance to explore Windows Forms a little.)

The ASP.NET version will demonstrate using custom-drawn bitmaps on Web pages—something that's very hard to do in most Web programming systems, but easy with ASP.NET. Dr. GUI thinks you'll like it. Run the applicationhttp://drgui.coldrooster.com/drguidotnet/3/GraphicalASPX to try it out.

Putting It All Together: The Drawing Classes

Last time we discussed using some pretty abstract examples, inheritance, interfaces, and polymorphism. The good doctor thought that this time we'd do something different and take a look at a small but somewhat realistic sample that demonstrates all that we've learned. And, since the application really needs graphics, that'll give us a chance to explore some new things: Windows Forms, as well as a very cool technique for creating bitmaps on the fly for display in Web pages—all without any client-side script!

The Canonical Polymorphism Example

In teaching programming, there is a set of pretty standard example programs that are commonly used. When Dr. GUI started out, he swore he'd never use any of them: that he'd never use a string class as an example, nor complex numbers, nor a drawing application. After all, doing so just wouldn't be original.

Well, as it turns out, there's a good reason (besides sloth) that these examples are used: they're very rich, very easy to explain and understand, and about as simple as possible while clearly demonstrating the crucial concepts.

So long ago, the good doctor capitulated and started using the "canonical" samples. Thus, for this column we have the granddaddy of polymorphism examples: the polymorphic drawing program.

Here's a screen shot of what the Windows Forms version of this program looks like:

Figure 1. Windows Form version of canonical polymorphism example

And here's what the ASP.NET version looks like in the browser:

Figure 2. ASP.NET version of canonical polymorphism example

You can run the ASP.NET version shown above.

What We're Doing Here

The basic idea of this program is this: we have an abstract base class (MustInherit in Microsoft® Visual Basic®) that contains the common data, such as bounding rectangle, and a set of virtual methods, mostly abstract (MustOverride in Visual Basic), such as Draw. Note that it's important that Draw be polymorphic since each type of drawable object, such as point, line, rectangle, circle, etc., is drawn with entirely different code.

While methods can be polymorphic, data cannot. Therefore, we put only the data in the program that truly applies to all possible drawable objects—in this case, a bounding rectangle and the color in which to draw the line(s) of the object.

Data specific to a particular type of drawable object, such as the center and radius of a circle, the coordinates of the opposite points of a rectangle, or the endpoints of a line, should be declared in the particular class (derived from the abstract base class) that corresponds with that type of drawable object. Note that it is possible to use a second level of derivation (or even several deeper levels) to consolidate similarities. For instance, circle could be derived from ellipse, since all circles are ellipses. Similarly, squares could be derived from rectangles, since all squares are also rectangles (and also quadrilaterals, and also polygons). The derivation tree you pick will ideally reflect not only the relationships between the classes but also the common expected usage patterns, so that operations you'll perform frequently are fast and easy.

Below is a diagram with our class derivation:

Figure 3. Class derivation diagram

Since the main reason for constructors (New in Visual Basic) exist is to initialize the data, constructors aren't (in fact, can't be) polymorphic, either. That means that the original creation operation can't be polymorphic, which makes sense since the data requirements vary from type to type. However, with good design, once the object is created, it can be treated for the rest of its life as a polymorph, as we do here.

Let's take a look at what's in this set of classes, starting with the root abstract base class:

The Abstract (MustInherit) Base Class

Here's the code for the abstract base class in C#. Dr. GUI calls it DShape for "drawable shape." Click to see the entire source file.

C#

public abstract class DShape {
   public abstract void Draw(Graphics g);
   protected Rectangle bounding;
   protected Color penColor; // should have property, too
   // should also have methods to move, resize, etc.
}

And here's the equivalent code in Visual Basic .NET. Click to see the entire source file.

Visual Basic .NET

Public MustInherit Class DShape
    Public MustOverride Sub Draw(ByVal g As Graphics)
    Protected bounding As Rectangle
    Protected penColor As Color ' should have property, too
    ' should also have methods to move, resize, etc.
End Class

The syntax is different, but it's clear that it's exactly the same class.

Note that the Draw method is implicitly virtual (Overridable) because it's declared abstract (MustOverride). Note also that we do not provide an implementation in this class. Since we don't know what object we're implementing in this class, how could we possibly write drawing code?

What's the data?

Note also that there isn't much data—but we've declared all the data that we can for such an abstract class.

Every drawable object, no matter what its shape, has a bounding rectangle—that is, the smallest possible rectangle which can completely enclose the object. The bounding rectangle is used for drawing points (as very small rectangles), rectangles, and circles—and it's useful for other shapes as a first very quick approximation for hit or collision testing.

There's not much else that all objects have in common; while the center makes sense for some objects, such as circles and rectangles, it doesn't make sense for others, such as triangles. And you'd want to represent a rectangle by its corners, not its center. But you can't specify a circle by its corners, since it has no corners. (For this application, we'll actually represent the circle with its bounding rectangle, since we have no need for the center or radius once the circle is created, and since the method we'll use to draw the circle, DrawEllipse, requires a bounding rectangle to know where to draw the circle.) Dr. GUI trusts that you see the difficulty in specifying other data for a generic drawable object. Also notice that since the data is private, we can use exactly the data that makes sense for our object without worrying about showing weird data to the users.

Every drawable object also has a color associated with the lines used to draw it, so we declare that here, as well. Note that we didn't declare the color for fillable objects, since not all objects are fillable. (We'll still make it possible for us to share implementation as well as interface, as you'll see later.)

Some Derived Classes

As noted, we can't actually create an object of the abstract base class's type, although we can treat any object derived from the abstract base class (or any base class) as though it was a base class object.

So in order to create an object to draw, we'll have to derive a new class from the abstract base class—and be sure to override all of the abstract/MustOverride methods.

The example we'll see here is for the DHollowCircle class. The DHollowRectangle class and the DPoint class are very similar.

Here's DHollowCircle in C#. Click to see the other classes.

C#

public class DHollowCircle : DShape 
{
   public DHollowCircle(Point p, int radius, Color penColor) {
      p.Offset(-radius, -radius); // need to convert to upper left
      int diameter = radius * 2;
      bounding = new Rectangle(p, new Size(diameter, diameter));
      this.penColor = penColor;
   }

   public override void Draw(Graphics g) {
      using (Pen p = new Pen(penColor)) {
         g.DrawEllipse(p, bounding);
      }
   }
}

And here's the same class in Visual Basic .NET. Click to see the other classes.

Visual Basic .NET

Public Class DHollowCircle
    Inherits DShape

    Public Sub New(ByVal p As Point, ByVal radius As Integer, _
            ByVal penColor As Color)
        p.Offset(-radius, -radius) ' need to convert to upper left
        Dim diameter As Integer = radius * 2
        bounding = New Rectangle(p, New Size(diameter, diameter))
        Me.penColor = penColor
    End Sub

    Public Overrides Sub Draw(ByVal g As Graphics)
        Dim p = New Pen(penColor)
        Try
            g.DrawEllipse(p, bounding)
        Finally
            p.Dispose()
        End Try
    End Sub
End Class

Note that we didn't declare any additional data for this class—as it turns out, the bounding rectangle and the pen are enough. (That happens to be true for points and rectangles, too; it wouldn't be true for triangles and other polygons, however.) Our application doesn't need to know the center or radius of the circle after it is set, so we ignore them. (If we really needed them, we could either store them or calculate them from the bounding rectangle.)

But we absolutely need the bounding rectangle, since it is a parameter to the Graphics.DrawEllipse method we're using to draw the circle. So we calculate the bounding rectangle from the center point and radius we were passed in the constructor. This is an example of how encapsulating the data helps allow you to make the best choices in data representation for your situation. The user of this class knows nothing of bounding rectangles, yet since we store only the bounding rectangle, our drawing code is more efficient and we store only one representation of the circle.

Let's look at each of the methods in depth.

The constructor

The constructor takes three parameters: a point that contains the coordinates for the center of the circle, the radius of the circle, and a System.Drawing.Color structure that contains the color in which to draw the circle's outline.

We then calculate the bounding rectangle from the center and radius, and set our pen color member to the color object we were passed.

The Draw-ing code

The Draw method overload is actually quite simple: it creates a pen object from the color object we saved in the constructor, then uses that pen to draw the circle using the Graphics.DrawEllipse method, passing also the bounding rectangle we created earlier.

Graphics and Pens and Brushes, oh MY!

The Graphics, Pen, and Brush objects deserve a little explanation. (We'll see the Brush objects shortly when we start filling our fillable objects.)

The Graphics object represents a virtualized drawing space associated with some real drawing surface. By virtualized, we mean that you use the same Graphics methods to draw on whatever type of surface you actually have associated with the Graphics object on which you're drawing. For those of you who are used to programming in MFC or the Microsoft Windows® SDK, the Graphics object is the .NET version of what's called a "Device Context" (or "DC") in Windows.

In this Windows Form application, the Graphics object that's passed into Draw will be associated with a window on the screen—specifically, our PictureBox. When we use this exact same code with our Microsoft ASP.NET application, the Graphics object Draw is passed will be associated with a bitmap image. It could also be associated with a printer or other devices. In essence, Graphics behaves like an abstract base class—it defines the interface, but doesn't constrain the implementations. The different ways of getting a Graphics object give you different types of objects with different behavior, but all implement the same core interface.

The advantage of this scheme is that we can use the exact same drawing code to draw on different types of surfaces. And in our drawing code, we don't need to know anything about the differences between screens, bitmaps, printers, and so on—the .NET Framework (and the underlying operating system, device drivers, and so on) take care of all the details for us.

By the same token, pens and brushes are virtualized drawing tools. A pen represents the attributes of a line—color, width, pattern, and perhaps even a bitmap that can be used to draw the line. A brush represents the attributes of a filled area—color, pattern, and perhaps even a bitmap used to fill an area.

Cleaning up after yourself using using (or at least Dispose)

Graphics, Pen, and Brush objects are all associated with Windows objects of a similar type. These Windows objects are allocated in the operating system's memory—memory that's not managed by the .NET runtime. Leaving these objects allocated for long periods of time can cause performance problems, and can even cause painting problems under Microsoft Windows 98 as the graphics heap fills up. As a result, you should release the Windows objects as quickly as possible.

You may be wondering why, if the .NET runtime provides memory management, you have to manage these objects yourself. Won't the runtime eventually free the objects?

The .NET runtime will indeed free the Windows objects automatically when the corresponding .NET Framework object is finalized and garbage collected. However, it might take a long time for the garbage collection to happen—and all sorts of bad things that won't trigger a garbage collection, including filling up Windows heaps and using up all the database connections, can occur if we don't free these objects promptly. This would be especially bad in the ASP.NET version of this application since many users could be accessing the application on the same server box.

Because these objects are associated with unmanaged resources, they implement the IDisposable interface. This interface has one method, Dispose, that detaches the .NET Framework object from the Windows object and frees the Windows object, thus keeping your computer happy.

You have but one obligation: to be sure to call Dispose when you're done with the object.

The canonical form for this is shown in the Visual Basic .NET code: you create the object, then use it in a Try block, then dispose of the object in the Finally block. The Try/Finally assures that the object will be disposed even if an exception is thrown. (In this case, the drawing method we call probably can't throw an exception, so the Try/Finally block probably isn't necessary. But it's good practice to get into, and the good doctor wanted to show you the correct form.)

This pattern is so common that C# has a special statement for it: using. The using statement in the C# code is exactly equivalent to the declaration and Try/Finally in the Visual Basic .NET code—but much neater, more convenient, and less error-prone. (Why Visual Basic .NET didn't include something like the using statement is totally beyond Dr. GUI.)

The Interface

Some, but not all, drawable objects can be filled. Objects such as points and lines can't be filled since they're not enclosed regions, while objects such as rectangles and circles can be either hollow or filled.

On the other hand, it would be handy, just as we can treat all drawable objects as polymorphs, to be able to treat all fillable objects as polymorphs. For instance, just as we put all of the drawable objects in a collection and draw them by iterating through the collection and calling Draw on each object, it would also be handy to be able to fill all the fillable objects in a collection, regardless of the actual type of the fillable object. So somehow, we'd like to use some mechanism like inheritance to achieve true polymorphism (in one lifetime, nonetheless!).

Since not all drawable objects can be filled, we can't put the Fill method declaration in the abstract base class. The .NET Framework doesn't allow multiple inheritances of classes, so we can't put it in another abstract base class, either. And if we derive the fillable objects from a different base class than the non-fillable ones, then we'd lose the ability to treat all drawable objects as polymorphs.

But the .NET Framework does support interfaces—and a class can implement any number of interfaces. The interfaces can't have any implementation—no code, nor any data. The class that implements the interface has to provide everything.

All the interface can have is declarations. And here's what our interface, IFillable, looks like in C#. Click to see the entire source file.

C#

public interface IFillable {
   void Fill(Graphics g);
   Color FillBrushColor { get; set; }
}

The equivalent Visual Basic .NET code is shown below. Click to see the entire source file.

Visual Basic .NET

Public Interface IFillable
    Sub Fill(ByVal g As Graphics)
    Property FillBrushColor() As Color
End Interface

We don't need to declare the method nor the property virtual/Overridable or abstract/MustOverride or anything, since all methods and properties in interfaces are automatically public and abstract/MustOverride.

Use a property: Can't have data in an interface

Note that while we can't declare a field in an interface, we can declare a property, since properties are actually implemented as methods.

But doing so puts a burden on the implementer of the interface, as we'll see shortly. The implementer has to implement the get and set methods, as well as whatever data is necessary to implement the property. If the implementation is very complex, you might be able to write a helper class to encapsulate some of it. We'll show how you might use a helper class later in the article in a slightly different context.

Implementing the Interface

Now that we have the interface defined, we can implement it in our classes. Note that we have to provide complete implementations of the interfaces we implement: we can't pick and choose the parts we want to implement.

So let's look at the code for the DFilledRectangle class in C#. Click to see the code for all the classes (including DfilledCircle).

C#

public class DFilledCircle : DHollowCircle, IFillable 
{
   public DFilledCircle(Point center, int radius, Color penColor, 
         Color brushColor) : base(center, radius, penColor) {
      this.brushColor = brushColor;
   }

   public void Fill(Graphics g) {
      using (Brush b = new SolidBrush(brushColor)) {
         g.FillEllipse(b, bounding);
      }
   }
   protected Color brushColor;
   public Color FillBrushColor {
      get {
         return brushColor;
      }   
      set {
         brushColor = value;
      }
   }
   public override void Draw(Graphics g) {
      Fill(g);
      base.Draw(g);
   }
}

And here's the code for the DFilledRectangle class in Visual Basic .NET. (Click to see the code for all the classes (including DFilledCircle).

Visual Basic .NET

Public Class DFilledRectangle
    Inherits DHollowRectangle
    Implements IFillable
    Public Sub New(ByVal rect As Rectangle, _
            ByVal penColor As Color, ByVal brushColor As Color)
        MyBase.New(rect, penColor)
        Me.brushColor = brushColor
    End Sub
    Public Sub Fill(ByVal g As Graphics) Implements IFillable.Fill
        Dim b = New SolidBrush(FillBrushColor)
        Try
            g.FillRectangle(b, bounding)
        Finally
            b.dispose()
        End Try
    End Sub
    Protected brushColor As Color
    Public Property FillBrushColor() As Color _
      Implements IFillable.FillBrushColor
        Get
            Return brushColor
        End Get
        Set(ByVal Value As Color)
            brushColor = Value
        End Set
    End Property
    Public Overrides Sub Draw(ByVal g As Graphics)
        Dim p = New Pen(penColor)
        Try
            Fill(g)
            MyBase.Draw(g)
        Finally
            p.Dispose()
        End Try
    End Sub
End Class

Following are some things to note about these classes.

Derived from HollowRectangle

We derived the filled class from the hollow version of the class. Most everything changes in this class: the Draw method and constructor are new (but both call the base class's versions) and we've added implementation for the Fill method of the IFillable interface and an implementation for the FillBrushColor property.

The reason that we need a new constructor is that we've got additional data in this class that needs to be initialized, namely the fill brush. (You'll recall our discussion of brushes above.) Note how this constructor calls the base class constructor: in C#, the "call" is built into the declaration (: base(center, radius, penColor)); in Visual Basic .NET, you make the call explicitly as the first line (MyBase.New(rect, penColor)) of the New method (aka "the constructor").

Since we've passed two of the three parameters to the base class constructor, all we have to do is initial the final field.

How Draw changes

You'll note that the Draw method is pretty much the same as the base class—the main exception being that it calls the Fill method, since doing a complete job of drawing a filled object involves filling it. Rather than rewriting the code for drawing the outline, we again call the base class's method: MyBase.Draw(g) in Visual Basic .NET, and base.Draw(g); in C#.

Since we're allocating a pen with which to draw the outline, we have to use using or Try/Finally and Dispose to make sure the Windows pen object is released promptly. (Again, if we were absolutely positive that the methods we're calling would never throw an exception, we could skip the exception handling and simply call Dispose when we're done with the pen. But we must call Dispose, whether directly or via the using statement.)

Implementing the Fill method

The Fill method is simple: allocate a brush, then fill the object on the screen—and make sure that we Dispose the brush.

Note that in Visual Basic .NET, you have to explicitly specify that you're implementing a method of an interface ("... Implements IFillable.Fill") while in C#, the fact that you're implementing a method or property in an interface is determined by the signature of the method or property (since you wrote a method called "Fill" that returns nothing and takes a Graphics, that MUST be the implementation of IFillable.Fill). Oddly enough, Dr. GUI, who generally prefers concise programming structures (if not concise writing), actually prefers Visual Basic's syntax because it's both clearer and more flexible (the name of the method in the implementing class in Visual Basic doesn't have to match the name in the interface, and a given method could conceivably implement more than one interface method).

Implementing the property

The IFillable interface also includes a property that allows us to set and get the brush color. (We use this in the Change fills to hot pink button handler.)

In order to implement the public property, we need to have a private or protected field. We made it protected here to ease access from derived classes without allowing any class to access it.

Once we have the field, it's easy to write a very simple pair of set and get methods to implement the property. Note once again that in Visual Basic .NET, you have to explicitly specify what property you're implementing.

You'll also notice that the implementation of this property is identical for every class. Later on, we'll see how to use a helper class to provide this implementation. In this case, the benefit is minimal (common code for the implementation can make changes easier). But in cases where the implementation of the property is more complex, the benefits of using a helper class can be great.

Interface or Abstract (MustInherit) Base Class?

One of the most common arguments in object-oriented programming is whether to use abstract base classes or interfaces.

Interfaces give you some additional flexibility, but at a cost: you have to implement everything in every class that implements that interface. We can use a helper class to assist with this (we'll have a related example later), but you still have to implement everything everywhere. And interfaces can't contain data (although, unlike in Brand J's system, they can contain properties, so they can look as though they contain data).

In this case, the good doctor chose to use an abstract base class rather than an interface for DShape because he didn't want to re-implement the data as properties in each class. Also, he chose a class rather than an interface because everything derived from DShape "is a" shape, where as the fillable objects are still shapes that also "can do" filling.

Your mileage may vary, of course, but Dr. GUI thinks he chose pretty well for this situation.

A Container for Our Drawing Objects

Since we're going to be drawing our objects repeatedly (each time the image paints in the Windows Forms version; each time the Web page is reloaded in the ASP.NET version), we need to keep them in a container so we can access them again and again.

Dr. GUI went a step further and made the containers smart containers that know how to draw the objects contained inside. Here's the C# code for this container class:

C# (click to see the entire file)

public class DShapeList {
   ArrayList wholeList = new ArrayList();
   ArrayList filledList = new ArrayList();

   public void Add(DShape d) {
      wholeList.Add(d);
      if (d is IFillable) 
         filledList.Add(d);
   }

   public void DrawList(Graphics g) {
      if (wholeList.Count == 0) 
      {
         Font f = new Font("Arial", 10);
         g.DrawString("Nothing to draw; list is empty...", 
            f, Brushes.Gray, 50, 50);
      }
      else 
      {
         foreach (DShape d in wholeList)
            d.Draw(g);
      }
   }

   public IFillable[] GetFilledList() {
      return (IFillable[])filledList.ToArray(typeof(IFillable));
   }   
}

And here's the Visual Basic .NET code for the same class:

Visual Basic .NET (click to see the entire file)

Public Class DShapeList
    Dim wholeList As New ArrayList()
    Dim filledList As New ArrayList()

    Public Sub Add(ByVal d As DShape)
        wholeList.Add(d)
        If TypeOf d Is IFillable Then filledList.Add(d)
    End Sub

    Public Sub DrawList(ByVal g As Graphics)
        If wholeList.Count = 0 Then
            Dim f As New Font("Arial", 10)
            g.DrawString("Nothing to draw; list is empty...", _
               f, Brushes.Gray, 50, 50)
        Else
            Dim d As DShape
            For Each d In wholeList
                d.Draw(g)
            Next
        End If
    End Sub

    Public Function GetFilledList() As IFillable()
        Return filledList.ToArray(GetType(IFillable))
    End Function
End Class

Maintaining two lists

Since we're going to want to change the fill color of our objects to implement that stylish Change fill to hot pink button, we maintain two lists of drawable objects: one list of *ALL* the objects, and one list of just the fillable objects. We use the ArrayList class for both of these. ArrayList objects hold a set of Object references—so an ArrayList can hold a mixed bag of any type in the system.

That's actually not helpful—we'd prefer that the ArrayLists contain only drawable/fillable objects. In order to do this, we make the ArrayList objects private; then we make the way to add objects to the lists a method that we write to take only a DShape.

When we add objects to the list using the Add method, we add every object into the wholeList and then check to see if the object should also be added to the filledList collection as well.

Remember that the Add method (and therefore the list) is typesafe: it only takes a DShape (or some type derived from DShape, such as all the types we created above). You can't add an integer or a string into this list; so we always know that this list only contains drawable objects. Being able to make that assumption is very handy! Strong typing is a good thing—a very good thing.

Drawing the items

We also have a method called DrawList that draws the objects in the list on the Graphics object it is passed as a parameter. This method has two cases: if the list is empty, it draws a string saying that the list is empty. But if the list isn't empty, it uses a for each construct to iterate through the list, calling Draw on each object. The code for the actual iteration and drawing almost couldn't be simpler, as shown in Visual Basic below.

Visual Basic .NET

Dim d As DShape
For Each d In wholeList
    d.Draw(g)
Next

The C# code is almost identical (but, of course, it takes fewer lines!).

C#

foreach (DShape d in wholeList)
   d.Draw(g);

Since we know that the list is type-safe because it's encapsulated, we know it's safe to just call the Draw method without checking the object's type.

Returning the fillable list

Finally, our Change fills to hot pink button needs an array of references to all the fillable objects so it can change their FillBrushColor property. Although it would be possible to write a method that iterated the list and changed the color to the value passed in, for this time the good doctor chose to return an array of object references instead. Luckily, the ArrayList class has a ToArray method that allows us to create an array that we pass back. This method takes the type that we want the array elements to be—thus allowing us to pass back the type we want—an array of IFillable.

C#

public IFillable[] GetFilledList() {
   return (IFillable[])filledList.ToArray(typeof(IFillable));
}   

Visual Basic .NET

Public Function GetFilledList() As IFillable()
    Return filledList.ToArray(GetType(IFillable))
End Function

In both languages, we use a built-in operator to get the Type object for a given type—in C#, we say "typeof(IFillable)", in Visual Basic, "GetType(IFillable)".

The calling program uses this array to iterate over the array of references to fillable objects. For instance, the Visual Basic code to change the fill colors to hot pink looks like this:

Dim filledList As IFillable() = drawingList.GetFilledList()
Dim i As IFillable
For Each i In filledList
    i.FillBrushColor = Color.HotPink
Next

This method is for demonstration purposes. The good doctor would hesitate before using this method in code he shipped. Why? Well, this design is questionable because it returns references to the list's internal data. (The array returned, however, is a copy of filledList, so at least the caller can't mess with that. But the references are still to the actual drawable objects.) It's usually better to provide a method to make the changes in a way you can check, rather than handing out references so the caller can do anything they want.

So why didn't Dr. GUI design this differently? First off, the idea is to demonstrate the polymorphism of using an object through a reference typed to its interface.The only thing the caller knows about the objects is that they implement IFillable. Second, it's hard to predict all the ways someone might want to manipulate the objects, so it's sometimes desirable to provide a method that returns references to internal data so that the caller has more flexibility. Like all engineering problems, there are trade-offs involved here—in this case, flexibility versus breaking encapsulation.

Helper Methods and Classes for Factoring Common Code

You might have noticed that the Draw and Fill methods have a lot of code in common Specifically, the code to create a pen or brush, set up a Try/Finally block, and dispose of the pen or brush is the same in every class—the only difference is the actual method called to do the drawing or filling. (Because of the very clean using syntax in C#, the amount of extra code isn't as obvious.) In Visual Basic .NET, there's one unique line of code to five lines of code that's identical in all implementations

You might also have noticed that the implementation of the property in IFillable is always identical.

In general, when you have a lot of repeated code, you want to look for ways to factor out the common code into common subroutines that are shared by all classes. Dr. GUI is pleased to show you three (of many) possible methods for doing this. The first method is an option for classes only; the second will work with either classes or interfaces, although we use it only with an interface in this example.

A note: We only did this code in Visual Basic, and only for the ASP.NET version of the application. But we could use the modified code in the Windows Forms applications with no problem. Dr. GUI did only one version so you could see the differences as you looked at the code.

Using calls to a helper class to replace identical code

The first problem we'll deal with is that the property implementation for IFillable is the same in all the classes that implement it. What we'll do is factor out the common code into a helper class, then create and call the helper in our code. In this case, creating and calling the helper takes as much code (actually a bit more) as just doing the work directly. It's also a little slower. But for a more complicated implementation of the property, this technique might make sense. In addition, it's easier to maintain code that's been factored into helpers; if you need to make a change to the helper, you change it in one place, not every place the code occurs. In some cases, that advantage might make using a helper class a good choice.

Systems such as C++ that support multiple inheritance of implementation (not just interfaces) can allow you to write helper classes with mix-ins that are inherited. But this is harder in a single-inheritance-only system like the .NET Framework—we'll have to directly instantiate the helper class and explicitly access it.

First, let's look at our helper class (click to see all the code):

<Serializable()> _
Public Class FillPropHelper
    Private brushColor As Color

    Sub New(ByVal brushColor As Color)
        Me.brushColor = brushColor
    End Sub

    Property FillBrushColor() As Color
        Get
            Return brushColor
        End Get
        Set(ByVal Value As Color)
            brushColor = Value
        End Set
    End Property
End Class

We'll talk about the <Serializable()> attribute later on...please ignore it for now.

It's easy to look at this class and see it as the implementation of a property. Again, this isn't very interesting as factored code, but if it was more complicated, or if you thought you'd be likely to want to modify the helper and have the changes used by all the classes that use the helper, you might want to use this technique even for a helper class that's this simple. But doing so will make your code somewhat bigger and slower.

In our client class (DFilledCircle), we have to declare and instantiate the helper object:

    Protected fb As FillPropHelper

    Public Sub New(ByVal center As Point, ByVal radius As Integer, _
            ByVal penColor As Color, ByVal brushColor As Color)
        MyBase.New(center, radius, penColor)
        fb = New FillPropHelper(brushColor) ' uses property helper
    End Sub

We still have to implement the property in our class, since it's part of the IFillable interface we're implementing. This implementation just accesses the property in the helper class.

    Public Property FillBrushColor() As Color Implements _
      IFillable.FillBrushColor
        Get
            Return fb.FillBrushColor
        End Get
        Set(ByVal Value As Color)
            fb.FillBrushColor = Value
        End Set
    End Property

Again, note that in this particular simple case, we didn't save any code in the client class that uses the helper—and we created an extra object, and made method calls rather than accessing a field directly in our class. (Compare with DFilledRectangle.) Because this particular implementation doesn't save any code and is slower, Dr. GUI probably wouldn't use it even though it is a bit more flexible if you need to modify the code that deals with the property. But in more complex cases, this technique can be very valuable.

Using common code when the code isn't identical

Approach 1: Common entry point calls virtual method

The next problem we'll attack is that the Draw and Fill methods are almost, but not quite, identical. This means we have to have a way to do different behavior. Virtual methods provide one way to do that, delegates provide another. We'll use one technique for Draw and a different method for Fill.

In this first approach, we'll also take advantage of the fact that abstract/MustInherit classes, unlike interfaces, can contain code. So we provide an implementation of the Draw method that creates a pen along with the exception handler and Dispose and then calls an abstract/MustOverride method that actually does the drawing. Specifically, the DShapes class changes to accommodate this new Draw method and to declare the new JustDraw method (click to see all the code):

Public MustInherit Class DShape
    '   The fact that Draw is NOT virtual is somewhat unusual...usually 
    ' Draw would be abstract (MustOverride).
    '   But this method is the framework for drawing, not the 
    ' drawing code itself, which is done in JustDraw.
    '   Note also that this means that these classes has a different 
    ' interface than the original version, although they 
    ' do the same thing.
    Public Sub Draw(ByVal g As Graphics)
        Dim p = New Pen(penColor)
        Try
            JustDraw(g, p)
        Finally
            p.Dispose()
        End Try
    End Sub
    ' Here's the part that needs to be polymorphic--so it's abstract
    Protected MustOverride Sub JustDraw(ByVal g As Graphics, _
        ByVal p As Pen)
    Protected bounding As Rectangle
    Protected penColor As Color ' should have property, too
    ' should also have methods to move, resize, etc.
End Class

An interesting note: the Draw method is not virtual/Overridable. Since all derived classes will do this part of the drawing in the exact same way (if you're drawing on a Graphics, which by definition we are, you'll absolutely have to allocate and dispose of a pen), there's no need for it to be virtual/Overridable.

In fact, Dr. GUI would argue that in this case, Draw should not be virtual/Overridable. If it were conceivable that you'd want to override Draw's behavior (and not just the behavior of JustDraw), you'd want to make it virtual/Overridable. But in this case, there's no good reason to ever override Draw's behavior, and there's a danger in encouraging programmers to override it—they might not deal with the pen correctly, or they might do something other than call JustDraw to draw the object, thus breaking assumptions we've built into our class. So making Draw non-virtual (an option we don't even have in Brand J, by the way) makes our code perhaps less flexible but also more robust—Dr. GUI thinks that in this case, this is the right trade-off.

A typical implementation of JustDraw looks like this (this one is from the DHollowCircle class; click to see all the code):

Protected Overrides Sub JustDraw(ByVal g As Graphics, ByVal p As Pen)
    g.DrawEllipse(p, bounding)
End Sub

As you can see, we've achieved the simplicity in the derived class implementations we were looking for. (The implementations in the fillable classes are only slightly more complicated—we'll see that later.)

Note that we've added an additional public method to our interface called JustDraw that takes, in addition to the Graphics object on which to draw, a reference to the Pen object we created in Draw. Since this method needs to be abstract/MustOverride, it must be public.

This isn't a huge problem, but it is a change to the public interface of the class. So even though this approach of factoring out common code is simple and convenient, you might choose the other approach to avoid having to change the public interface.

Approach 2: Virtual method calls common helper method, using callback

When it comes time to implement the Fill method of the interface, the complication of the code is similar: there's one unique line out of six lines of implementation. But we can't put the common implementation into the interface, since interfaces are purely declarations; they contain no code nor data. In addition, the approach outlined above isn't acceptable because it would change the interface—we might not want to do that, or we might even not be able to do that because someone else created the interface!

So what we need to do is to write a helper method that does the setup and then calls back into our class to allow it to actually do the fill. For this example, Dr. GUI put the code in a separate class so that any class could use it. (If you were taking the approach for implementing Draw, you might want to implement the helper method as a private method in the abstract base class.)

Without further ado, here's the class we created (click to see all the code):

' Note how the delegate helps us still have polymorphic behavior.
Class FillHelper
    Public Delegate Sub Filler(ByVal g As Graphics, ByVal b As Brush)
    Shared Sub SafeFill(ByVal i As IFillable, ByVal g As Graphics, _
            ByVal f As Filler)
        Dim b = New SolidBrush(i.FillBrushColor)
        Try
            f(g, b)
        Finally
            b.dispose()
        End Try
    End Sub
End Class

Our helper method called SafeFill takes a fillable object (note we're using the IFillable interface type here, not DShape, so that only fillable objects can be passed!), a Graphics to draw on, and a special variable called a delegate. You can think of a delegate as a reference not to an object, but to a method—if you've programmed much in C or C++, you can think of it as a type-safe function pointer. The delegate can be set to point to any method, whether an instance method or a static/Shared method, that has the same types of parameters and return value. Once the delegate is set to point to an appropriate method (as it will be when SafeFill is called), we can call that method indirectly through the delegate. (By the way, since delegates aren't available in Brand J, this approach would be considerably more difficult and less flexible if you were using it.)

The declaration for the delegate type Filler is just above the class declaration—it's declared as a method that returns nothing (a Sub in Visual Basic .NET) and takes a Graphics and a Brush as parameters. We'll be discussing delegates more in-depth in a future column.

The SafeFill operation is simple: it allocates the brush and sets up the Try/Finally and Dispose as common code. It does the variable behavior by calling the method referred to by the delegate we received as a parameter: "f(g, b)".

To use this class, we'll need to add a method to our fillable object classes that can be called through the delegate, and be sure to pass a reference (the address) of that method to SafeFill, which we'll call in the Fill implementation of the interface. Here's the code for DFilledCircle (click to see all the code):

Public Sub Fill(ByVal g As Graphics) Implements IFillable.Fill
    FillHelper.SafeFill(Me, g, AddressOf JustFill)
End Sub
Private Sub JustFill(ByVal g As Graphics, ByVal b As Brush)
    g.FillEllipse(b, bounding)
End Sub

So when the object needs to be filled, IFillable.Fill is called on the object. That calls our Fill method, which calls FillHelper.SafeFill, passing a reference to our self, the Graphics object we were passed to draw on, and a reference to the method that will actually do the filling—in this case, our private JustFill method.

SafeFill then sets up the brush and calls, via the delegate, our JustFill method, which does the fill by calling Graphics.FillEllipse and returns. SafeFill disposes of the brush and returns to Fill, which returns to the caller.

Finally, JustDraw is very similar to Draw in the original version in that we both call Fill and call the base class's Draw method (this is what we did before). Here's the code for that (click to see all the code):

Protected Overrides Sub JustDraw(ByVal g As Graphics, ByVal p As Pen)
    Fill(g)
    MyBase.JustDraw(g, p)
End Sub

Remember that the complication of allocating the brushes and pens is dealt with in the helper functions—in the case of Draw, in the base class; in the case of Fill, in the helper class.

If you think that this is a bit more complex than before, you're right. If you think it's a little slower than before because of the extra calls and the need for dealing with a delegate, you're right again. Life is about trade-offs.

So, is it worth it? Maybe. It depends on how complicated the common code is and how many times it would be duplicated; in other words, it's a trade-off. If we'd decided to eliminate the Try/Finally and simply dispose of the pen and brush after we were done drawing with them, that code would have been simple enough that neither of these approaches would have been warranted (unless the maintenance advantage was important). And in C#, the using statement is simple enough that we probably wouldn't want to bother with these approaches, either. Dr. GUI thinks that the Visual Basic case using Try/Finally is on the borderline as to whether it's worthwhile to use one of these approaches or not, but he wanted to show these approaches to you in case you have situations with more complicated code.

Making Our Objects Serializable

We need to make one more change to our drawable objects classes in order to use them with ASP.NET: they need to be serializable so that the data can be passed between the main Web page and the Web page that generates the image (more on this later). Serialization is the process of writing the data for a class to a storage medium in a way that it can be stored and/or passed around and deserialized later. Deserialization is the process of re-creating the object from the serialized data. We'll discuss this more in-depth in a later column.

When Dr. GUI originally wrote this application as a Windows Forms application, he used only the stock brushes and pens available in the Brushes and Pens classes that are pre-allocated by the .NET Framework and the operating system. Since these are already allocated, keeping references to them doesn't hurt anything, and they don't need to be Disposed.

But pens and brushes, because they can be pretty complicated objects, are not serializable, so the good doctor had to change his strategy. He decided to store the color of the pens and brushes instead, and to create pens and brushes on the fly as the objects need to be drawn and filled.

How do you make things serializable?

Serialization is a big part of the .NET Framework, so the Framework makes it easy to serialize objects.

All we have to do to make a class serializable is to mark it with the Serializable attribute. (This is the same kind of attribute we used before on our enumeration to mark it as a set of flags.) The syntax in C# and Visual Basic .NET is as follows:

C#

[Serializable]
class Foo // ...

Visual Basic .NET

<Serializable()> _
Class Foo ' ...

A note: in addition to marking your class serializable, all of the data your class contains has to be serializable as well or the serialization framework will throw an exception when you attempt to serialize the data.

Making the container serializable, too

One of the cool things about the .NET Framework is that the container classes are serializable. That means that if you store objects in them that are serializable, the container will automatically be able to serialize them, too.

So in our case, the DShapeList class contains two ArrayList objects. Since ArrayList is serializable, all we have to do to make DShapeList serializable is to mark it with the Serializable attribute as follows:

Visual Basic .NET

<Serializable()> _
Public Class DShapeList
    Dim wholeList As New ArrayList()
    Dim filledList As New ArrayList()
    ' ...

C#

[Serializable]
public class DShapeList {
   ArrayList wholeList = new ArrayList();
   ArrayList filledList = new ArrayList();

Provided that the objects we place in the DShapeList are all serializable, we will be able to serialize and de-serialize the whole list with a single statement!

By the way, this would be a good change for the Windows Forms versions of the applications because it would allow us to write our drawings out to a disk file and load them back in.

Three versions of drawable objects; any can be used in any context

You've probably noted by now that we have three versions of our drawable objects code: one each in C# and Visual Basic .NET that do not use the helper methods we wrote just above, and one in Visual Basic .NET that does use the helpers.

There is one other minor difference: the data classes in the file with the helpers are marked as Serializable; the data classes in the other files are not.

But note this very important point: if we went back and marked the data classes in all the files Serializable, we'd be able to use any of the classes with any of the applications. We'd be able to mix C# and Visual Basic .NET. And we'd be able to use code originally written for a Windows Form application with an ASP.NET application.

This kind of easy code re-use means that the code you write is more valuable, since it can be reused in so many different places.

Using Our Drawable Objects in a Windows Forms Application

Now that we've discussed the drawable objects classes, let's talk about how we make use of them in a Windows Forms application. First, let's talk a little about how Windows Forms applications work.

Main Parts of a Windows Forms Application

Simple Windows Forms applications consist of a main window, or form, that contains children that are controls. If you're a Visual Basic programmer, you'll find this model very familiar.

The Main Window

The key object in any Windows Forms application is the main window. This form will be created in the static/Shared Main method of your application, as shown below.

In a simple Windows Forms application like we're writing, all of the other controls will be children of this main form.

Buttons and Text Boxes

Our form has a set of buttons and a few text boxes. Each button has a handler that causes a shape to be added to the list and the list to be drawn. The text boxes are included to show you how to get input from a form. And there's a group box to provide a visual indication about the text boxes and associated buttons.

PictureBox

On the left is the most important control of all: the PictureBox. This is where the image is drawn and displayed. In a Windows application, you need to be able to redraw the image at any time—for instance, if the window is minimized or covered by another window, you'll need to redraw when the window is again exposed.

This on-demand drawing is done in response to the Paint message, which is handled by an event handler in the parent form window class.

Main Routines in a Windows Forms Application

Let's take a quick look at the important routines in our Windows Forms application. Note that the code for the UI is quite short compared with the code for the drawable objects. That's the power of having the .NET Framework doing a lot of the work for you. (And it shows we did a pretty good job with the drawable objects classes.)

Form methods

The form, or main window, is derived from System.Windows.Forms.Form, so it inherits all of its behavior. All of the controls are declared as members of this class, so they'll be disposed when the class is disposed (which is actually done explicitly in the Dispose method).

It also contains declarations for the data we need (the DShapeList and a random number generator object), Main, and event handlers for button clicks and the paint event of the PictureBox.

Main

Main's job is simply to create and run the main window object. The code for this in C# is below.

C# (click to see all the code)

[STAThread]
static void Main() 
{
   Application.Run(new MainWindow());
}

The STAThread attribute is important for Main of Windows Forms applications—you should always use it so that functionality that relies on OLE Automation (such as drag-and-drop and the Clipboard) will work correctly.

You won't find this method in the Visual Basic .NET source code generated for you by Microsoft Visual Studio®; but if you look in the generated .exe using ILDASM, you'll find a Main that does the same thing as the one above—probably generated by the Visual Basic .NET compiler.

InitializeComponent

Under Windows Form Designer generated code (click on the little plus sign if you can't see the code in this region), you'll see the code for creating and initializing all of the buttons and other controls on the form. You can click to see all the code for C# and for Visual Basic .NET, including this region.

Data declarations/random number generation

In addition to all of the controls declared in the hidden region of your code, we also need to declare two variables: the data structure to hold our drawing list, and an object of type Random. We use the Random object to generate random numbers for the positions of objects we create. You can check the Random class reference documentation.

The data declarations are inside the MainWindow class but outside of any method. In C# and Visual Basic .NET, it looks like the following:

C#

DShapeList drawingList = new DShapeList();
Random randomGen = new Random();

Visual Basic .NET

Dim drawingList As New DShapeList()
Dim randomGen As New Random()

We also wrote a helper method to get a random point:

C#

private Point GetRandomPoint() {
   return new Point(randomGen.Next(30, 320), randomGen.Next(30, 320));
}

Visual Basic .NET

Private Function GetRandomPoint() As Point
    Return New Point(randomGen.Next(30, 320), randomGen.Next(30, 320))
End Function

It generates two random numbers between 30 and 320 to use as the coordinates for our random point.

Button click event handlers

The next thing we have is a button click handler for each button. Most of them just add a new drawable object to the drawing list and call Invalidate on the PictureBox, causing it to repaint using the updated drawing list. Code for a typical button handler is as follows:

C#

private void AddPoint_Click(object sender, System.EventArgs e)   {
    drawingList.Add(new DPoint(GetRandomPoint(), Color.Blue));
    Drawing.Invalidate();
}

Visual Basic .NET

Private Sub AddPoint_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles AddPoint.Click
    drawingList.Add(New DPoint(GetRandomPoint(), Color.Blue))
    Drawing.Invalidate()
End Sub

The Change fills to hot pink button is a little different—it gets an array of all the fillable objects in the list, then changes their brush color to hot pink. The code for that is shown above at the end of the Returning the fillable list section. (You also have to invalidate the PictureBox.)

Lastly, the Erase All button simply creates a new drawing list and makes our drawingList field point to it. By doing this, it releases the old drawing list for eventual garbage collection. It then invalidates the PictureBox so it's erased, too.

PictureBox paint event handler

The last item we have to take care of is painting the image in the PictureBox. To do this, we handle the Paint event that the PictureBox generates, and use the Graphics object we're passed by this event to draw on. To do the drawing, we just call the drawing list's DrawList method—a for each loop and polymorphism take care of the rest!

C# (Click to see all the code).

private void Drawing_Paint(object sender,
      System.Windows.Forms.PaintEventArgs e) {
   drawingList.DrawList(e.Graphics);
}

Visual Basic .NET (Click to see all the code).

Private Sub Drawing_Paint(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.PaintEventArgs) _
        Handles Drawing.Paint
    drawingList.DrawList(e.Graphics)
End Sub

This concludes our tour of the Windows Forms applications—play with the code and modify it some to learn more!

Using Our Drawable Objects in an ASP.NET Application

Although there are some differences between ASP.NET Web applications and Windows Forms applications, the amazing thing to Dr. GUI is how similar the two types of applications are! So if you're used to Visual Basic forms or Windows applications, this section should help you make the transition to ASP.NET Web applications.

By the way, once again, you can run the ASP.NET application.

Main Parts of a Web Forms Application

The main parts of the ASP.NET Web Forms application correspond closely to the parts of the Windows Forms application.

The Page

This corresponds to the main window in the Windows Forms application. The page is the container for all of the buttons and other controls.

Buttons

Again, there is a set of buttons that cause actions to be performed on the form. Note that, unlike in our previous applications, we set the pageLayout property of the page's document to GridLayout rather than FlowLayout. That means that we'll be able to position each button (and other control) exactly by pixel position.

Note that there are a few text boxes, too.

Note also that you can't copy and paste Windows Forms controls into a Web Form—you'll have to create the page all over again.

Image Control

The image control corresponds to the PictureBox in the Windows Forms application. However, there are some important differences: instead of generating Paint messages, the image control contains the URL from which we'll load our image.

We set this URL to be a second Web page, ImageGen.aspx. In other words, we have a Web page whose whole job is to generate the bits in the image from our drawing list, and to send that image to the client's Web browser.

We'll discuss the code for this below.

Main Routines of a Web Forms Application

There are some important differences between the code for Windows Forms applications and Web Forms applications—but there are some interesting similarities, too. And note that all the code in the drawable objects file can be used for any of the three applications.

Our page is derived from System.Web.UI.Page and has a bunch of declarations for all of the controls, in addition to:

All the same: Data declarations and GetRandomPoint

This code is almost the same as in the Visual Basic .NET Windows Forms application. If you like, take another look at it above. The one difference is that the fields are declared but not initialized. They'll be initialized in the Page_Load method (see below).

The GetRandomPoint method is exactly the same as in the other applications. Being able to reuse code is cool, isn't it?

Very similar: Button click handlers

The button click handlers are the same as in the Windows Forms applications with one exception: since the image will be redrawn each time a button is clicked on the Web form, there's no need (and no way) to "invalidate" the image control. Rather, it will be redrawn automatically—so the only call is to the drawing list's Add method.

Here's a typical button handler—this one for a point (click to see all the code):

Private Sub AddPoint_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles AddPoint.Click
    drawingList.Add(New DPoint(GetRandomPoint(), Color.Blue))
End Sub

The other button handlers are similar to the Windows Forms case, with the exception, of course, of not calling any sort of method to invalidate the image.

Very different: Page load and unload handlers

What's totally new is that page load and unload handler methods.

Remember that our page object is created anew with each HTTP request. Since it's created anew with each request, we cannot store data as member variables as in the Windows Forms case, where the main window exists as long as the application exists.

So we'll have to store the information we need between requests and across pages in some sort of state variable. There are several options here—we'll discuss this next.

Passing State Between Pages and Requests

In order for our application to work, it needs to be able to maintain state between requests and to pass that state to the drawing page (see below).

There are several ways of maintaining and passing state. If our application was strictly a single-page application (as previous applications were), you could use view state, where the data is encoded in a hidden input field in the Web page.

But our image control does its drawing in a separate page, so we need something more flexible. Our best choices are cookies and session state. Session state is very flexible, but requires server resources to use. The browser maintains cookies, but they're very limited in size.

Page_Load

Page_Load is called after the page object is created but before any event handlers are run. So our Page_Load method is a perfect place to attempt to load our persistent data. If we don't find it, we create new data. Here's the code:

    Private Sub Page_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) _
            Handles MyBase.Load
        randomGen = ViewState("randomGen")
        If randomGen Is Nothing Then randomGen = New Random
        ' Option 1: use Session state to get drawing list 
        ' (saved in Page_Unload)
        ' (Note: requires state storage on server)
        drawingList = Session("drawingList")
        If drawingList Is Nothing Then drawingList = New DShapeList

        ' Option 2: Session state using SOAP (or base 64) serialization
        ' Note: helpers convert to string; might be better to stop at
        ' stream or byte array....
        'Dim tempString As String = Session("drawinglist")
        'If tempString Is Nothing Then
        '   drawingList = New DShapeList
        'Else
        '    ' or DeserializeFromBase64String...
        '    drawingList =__
        '        SerialHelper.DeserializeFromSoapString(tempString)
        'End If

        ' Option 3: retrieve drawing state from cookie 
        ' on the user's browser
        ' (Note: no server storage, but some users disable cookies)
        ' (2nd note: deserialization is not automatic with cookies! :( )
        ' (3rd note: Using a cookie will severely limit the number 
        '   of(shapes you can draw)
        'Dim drawingListCookie As HttpCookie
        'drawingListCookie = Request.Cookies("drawingList")
        'If drawingListCookie Is Nothing Then
        '   drawingList = New DShapeList
        'Else
        '    drawingList = _
        '        SerialHelper.DeserializeFromBase64String( _
        '           drawingListCookie.Value)
        '    ' another option: DeserializeFromSoapString, but don't--
        '    ' see article
        'End If
    End Sub


First, we attempt to load the random number generator state from the view state. If it's there, we use the stored value. If it's not, we create a new Random object.

Next, we attempt to load the drawing list from the session state. Again, if there's no drawing list present, we create an empty one.

Both the view state and the session state automatically serialize our objects if need be. The view state is always serialized so it can be represented as an encoded string in a hidden input field in the browser. The session state is serialized if it's to be stored in a database or passed between servers, but it's not serialized if your application is running on a single server, as in when testing on your development machine.

The "Option 2" commented-out code would load the drawing list from the session state, but by using either SOAP serialization or Base64 encoding of binary serialization. In either case, this code converts the serialized stream into a string. This isn't necessary for saving your data in session state, since session state automatically serializes. However, it can be interesting to read the SOAP representation of your data—you might try this in the debugger sometime.

The "Option 3" commented-out code tries to load the drawing list from a cookie. Note that dealing with a cookie is considerably more complicated than dealing with view or session state. For one thing, you don't get automatic serialization. In order to serialize to a string, we wrote the helper function in a new class, as shown below (click to see all the code):

Public Shared Function DeserializeFromBase64String( _
        ByVal base64String As String) As Object
    Dim formatter As New BinaryFormatter()
    Dim bytes() As Byte = Convert.FromBase64String(base64String)
    Dim serialMemoryStream As New MemoryStream(bytes)
    Return formatter.Deserialize(serialMemoryStream)
End Function

Dr. GUI used a binary formatter and converted to a printable base 64 string because neither the SOAP nor XML formatters worked well for this serializing to cookies. We had to convert to a base 64 string from a pure binary representation to avoid potential problems with control characters in the string if we simply copied the bytes. The base 64 string uses one character A-Z, a-z, 0-9, + or / (that's 64, or 2^6, characters) to represent each six bits in the binary string, so four characters represent three bytes—the first character represents six bits in the first byte, the second character represents the last two bits of the first byte and the first four bits of the second byte, and so on. Again, the point is that by restricting our string to printable characters, we avoid any potential problems with control characters.

The XML formatter won't serialize private data—and Dr. GUI wasn't about to add public access to the private data in the drawing list. The SOAP formatter doesn't have that limitation, but produces such large strings that even a drawing object or two would overflow the capacity of a cookie. (Version 1.0 of the .NET Framework had a nasty bug where the SOAP formatter wouldn't serialize an empty list such that it could be deserialized. Instead, it wrote nothing to the stream for the empty list, causing an exception to occur when we attempted to deserialize, but that bug was fixed in Version 1.1.)

For readability, the good doctor would have preferred to serialize in a readable XML format, but since neither of the XML serialization formatters would do the job for cookies, he settled for the binary formatter and a conversion to a base 64 string.

Note, however, that the size restriction on cookies makes it impossible to save much data using them. Using session state usually works much better unless the data is very small.

Page_Unload

Page_Unload is called before the page object, including any contained data, is destroyed. So it's a great place to persist our important data so we can pick it up later in Page_Load (or in the image's Page_Load).

So we save the data in Page_Unload, and retrieve it in Page_Load. Seems a little strange, but it's the right thing to do.

Here's our code for Page_Unload (click to see all the code):

    Private Sub Page_Unload(ByVal sender As Object, _
            ByVal e As System.EventArgs) _
            Handles MyBase.PreRender
        ViewState("randomGen") = randomGen

        ' Option 1: write session state using automatic serialization
        Session("drawingList") = drawingList

        ' Option 2: session state using SOAP (or base 64) serialization
        ' Since session state can serialize anything, didn't have to 
         ' convert to string--but needed string for Option 3
         ' Session("drawingList") = _' (or use SerializeToBase64String)
         '   SerialHelper.SerializeToSoapString(drawingList)

        ' Option 3: write a cookie. Must write code to serialize.
        ' NOTE: Using a cookie will severely limit the number of shapes 
        ' you can draw--not even usuable with SOAP serialization because
        ' cookie size is limited and SOAP serialization takes a lot of
        ' space.
        'Dim drawingListString As String = _
        '    SerialHelper.SerializeToBase64String(drawingList)
        '' another option: SerializeToSoapString, but don't--see article
        'Response.Cookies.Add(New HttpCookie("drawingList", _
        '    drawingListString))
    End Sub

This code is somewhat simpler since we don't have to check to see if the state is already in the view or session state object; we just unconditionally write it out.

Again, while view state and session state serialize themselves automatically, cookies do not, so we need to do it ourselves. Dr. GUI wrote the following helper function (in a separate class), with the code shown here:

Public Shared Function SerializeToBase64String(ByVal o As Object) _
        As String
    Dim formatter As New BinaryFormatter()
    Dim serialMemoryStream As New MemoryStream()
    formatter.Serialize(serialMemoryStream, o)
    Dim bytes() As Byte = serialMemoryStream.ToArray()
    Return Convert.ToBase64String(bytes)
End Function

(There are also helper functions for SOAP serialization in the complete code).

Drawing in a Separate Page

As we mentioned before, the drawing is done in a separate page. Here's the code for that page (click to see all the code):

    Private Sub Page_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
        Dim drawingList As DShapeList

        ' Get the drawing list:
        ' Option 1: use session state w/ automatic serialization
        drawingList = Session("drawingList")
        If drawingList Is Nothing Then drawingList = New DShapeList

        ' Option 2: session state with SOAP/base 64 serialization
        ' (See main page code for more comments)
        'Dim tempString As String = Session("drawinglist")
        'If tempString Is Nothing Then
        '   drawingList = New DShapeList
        'Else
        '    ' or DeserializeFromBase64String...
        '    drawingList = _
        '       SerialHelper.DeserializeFromSoapString(tempString)
        'End If

        ' Option 3: use a cookie...
        ' (See main page code for more comments)
        'Dim drawingListCookie As HttpCookie
        'drawingListCookie = Request.Cookies("drawingList")
        'If drawingListCookie Is Nothing Then
        '   drawingList = New DShapeList
        'Else
        '    drawingList = _
        '        SerialHelper.DeserializeFromBase64String( _
        '        drawingListCookie.Value)
        '    ' another option: DeserializeFromSoapString, but don't--
        '    ' see article
        'End If

First, we get the drawing list from session state or a cookie. (The code for this is similar to the Page_Load method above. See the section above for an explanation.)

Next, we set the ContentType of the response stream we're writing to be a GIF image.

After that, we do something really funky: we create a bitmap of the exact size we want (in this case, the same size as in the Windows Forms application).

Then we get a Graphics object associated with that bitmap, clear it, and draw our list onto it.

Here's the important part: we next write the image contents in GIF format out to the response stream (i.e. to the browser). We set the response type to make sure the browser interprets the image properly, and then sends the bits of the image. (The .NET Framework makes it amazingly easy to do this. Just drawing on a bitmap was a royal pain in the old Windows GDI days!)

The other important part is to remember to dispose of the Graphics and Bitmap objects—and use a Try/Finally so the objects will get disposed even if an exception is thrown.

Phew! That was a lot, but it was worth it to get this application running as an ASP.NET application—and better yet, one that relies on no client-side script. Check it out! You can run the application.

Give It a Shot!

If you've got .NET, the way to learn it is to try it out. If not, please consider getting it. If you spend an hour or so a week with Dr. GUI .NET, you'll be an expert in the .NET Framework before you know it.

Be the First on Your Block—and Invite Some Friends!

It's always good to be the first to learn a new technology, but even more fun to do it with some friends! For more fun, organize a group of friends to learn .NET together!

Some Things to Try...

First, try out the code shown here. Some of it is excerpted from larger programs; it's good practice to build up the program around those snippets. (Or use the code the good doctor provides, if you must.) Play with the code some.

Add a few different shapes, filled and unfilled, to the drawing program. A note: although we've not covered it yet, the classes you add could be in a different assembly (and therefore a different executable file) from the other files. That means that the classes you add could even be in a different language than the rest. You get extra credit for doing the reading and work necessary to make this work.

In the Windows Forms version of this program, use serialization to write your data out to a file so you can save and load drawings. For a real challenge, figure out how to print.

Add some methods to the classes here, and fiddle with the user interface. Make yourself a cute little CAD program.

Use inheritance, abstract/MustInherit classes, interfaces, and polymorphism in a project of your own choosing. Inheritance works best when the family of classes has a great deal in common. Interfaces work best when the classes don't have much in common but the functionality is similar.

Try doing your own drawing apps in ASP.NET. Note that if you can run the .NET Framework, all you need is Microsoft Internet Information Server (IIS) in order to run ASP.NET on your very own machine—no server required! Dr. GUI thinks it's indescribably cool to be able to build and test web applications on his three-pound laptop using just the standard OS and the free .NET Framework. (But the good doctor prefers to use Visual Studio or at least Visual Basic or Microsoft Visual C#® Standard Edition as well.) Look Ma! No server! You don't even need an Internet connection! (Try that with Brand J's extended edition….)

Yo! Let's talk!

Do you have some questions or comments about this column? Come visit the Dr. GUI .NET message board on GotDotNet.

What We've Done; What's Next

This time, we saw a more complete example of inheritance, abstract (MustInherit) base classes, and interfaces in a simple drawing application—and we did it using both Windows Forms and ASP.NET Web Forms.

Now that we understand how inheritance, interfaces, and polymorphism all work, next time we'll delve into the mother of all .NET Framework classes: System.Object. If we have time, we'll also discuss memory management, including garbage collection and Dispose.

Show: