Find out how your knowledge of visual inheritance in Visual FoxPro can help you take advantage of Visual Studio .NET's slightly different model.
Visual inheritance, long one of Visual FoxPro's strong suits and much advertised features, lets you modify intrinsic controls and mold them to your liking. In Visual Studio .NET, inheritance is also available. In fact, .NET uses inheritance for all user interfaces, both in Windows Forms as well as ASP.NET Web Forms. Furthermore, .NET employs a slightly more consistent and powerful model than Visual FoxPro. However, this is less advertised, and not as well known in the .NET developer community, where inheritance features are just being discovered. As a Visual FoxPro developer, you have a great head start in that area, so we don't have to discuss the basics of inheritance. Let's start by comparing available features one by one.
Subclassing individual WinForm controls
In Visual FoxPro, subclassing visual controls is a straightforward task. You can use the CREATE CLASS command in the VFP Command window to launch a dialog that asks for a class name, the parent class, and the library in which the class will be stored. The parent class is a key setting because it defines the class that will be subclassed. For example, you can pick the Label class to create a subclassed label. You can then visually design the new label class, so you can set properties, override methods, and add new methods altogether. You can then use the new class in a fashion almost identical to the original label class.
In .NET, things are similar. The main difference is that in .NET, all classes are stored in source code, rather than .VCX files. Defining visual controls in source code is possible in VFP, but it isn't widely practiced. The reason is that classes defined in FoxPro source code files can't be edited visually. There's no property grid that lets you change properties, and there's no way to see what the designed class looks like. In .NET, you can visually edit most classes defined in source code. However, this is only partially true for individual UI controls. In general, subclassed controls don't have a visual appearance during initial design, but they support a property sheet.
To demonstrate how you can subclass individual controls in .NET, create a new class library project in Visual Studio .NET. Then, add a reference to the System.Windows.Forms assembly, which gives you access to all the classes that make up the Windows Forms namespace. You can then add a new class file called SampleLabel. Here's the C# version of that class:
public class SampleLabel : System.Windows.Forms.Label
{
public SampleLabel()
{
}
}
If you choose to create a Visual Basic .NET project instead, you should code your class like this:
Public Class SampleLabel
Inherits System.Windows.Forms.Label
Public Sub New()
End Sub
End Class
In either case, this creates a new label class that derives from the default label. So far, the new label is identical to the original label because you haven't made any changes. You can change properties of the label by setting them in the constructor. For example, you can set the label to auto-size by setting the appropriate property:
public class SampleLabel : System.Windows.Forms.Label
{
public SampleLabel()
{
this.AutoSize = true;
}
}
Again, the Visual Basic version is similar:
Public Class SampleLabel
Inherits System.Windows.Forms.Label
Public Sub New()
Me.AutoSize = True
End Sub
End Class
Now that you have a label with an altered behavior, you can compile your project and use the label. So, how can you add the label to a form? In Visual Studio .NET, you can use the new label by adding it to the Toolbox. To do this, simply right-click on the Toolbox and select "Add/Remove Items." This launches a dialog that lets you browse to the file you just compiled. Visual Studio .NET then looks for all classes within the selected file you can add to the Toolbox. In this case, the new label class is the only control that can display in a Toolbox, so Visual Studio automatically pre-selects it. All you have to do is click on the OK button. Figure 1 shows the new control ready for use in the Toolbox.
After the label is available in the Toolbox, you can simply drag and drop it on a form, just like the intrinsic label control. The only difference is your new label will auto-size, and the default label class won't. So far, things work similarly to how you know them to work in Visual FoxPro. In fact, for the most part, both systems behave similarly, and aside from the source code nature of .NET, Visual FoxPro developers will feel right at home. However, there are a few details worth discussing. The most important one is visible in the property sheet with the label object selected (figure 2).
However, for some reason, the property value displays in bold, indicating that Visual Studio thinks the value is a non-default value. This is a bit odd, because the default for your class is True. This is also rather inconvenient, because Visual Studio automatically creates code to set property values whenever they aren't set to their default values. For this reason, you'll find the following code in the InitializeComponent() method of the form you dropped the label control on:
Me.SampleLabel1.AutoSize = True
This is the Visual Basic version. If you drop your label on a C# form, you'll see this code:
this.SampleLabel1.AutoSize = true;
At first, this code seems unnecessary because the value is already set to True, which means the code will have no effect whatsoever. Unfortunately, this code can also have a negative impact that isn't immediately obvious: If you go back to the label class and change the property back to False, you'd expect all instances of the label to change their behavior. However, this isn't the case, because each form that contains an instance of the label has code that hard codes the auto-size value of all existing labels to True -- not the desired behavior at all!
This happens because VS.NET doesn't look at a property's value in the class that defines it to find out what the default value should be (as Visual FoxPro does). Instead, it looks at an attribute attached to the property. In the example, you only changed the property value, not its default setting. You can fix this by attaching a new attribute to the property definition (not surprisingly, the attribute is called "DefaultValue"). However, because the property is only set but not defined in your class, you don't have a good place to attach the attribute. The only way to fix the problem is to override the property, without really changing anything. (Note the attribute is defined in the System.ComponentModel namespace, which you may have to add to your source code file). Here's the C# version of your fixed label class:
public class SampleLabel : System.Windows.Forms.Label
{
public SampleLabel()
{
this.AutoSize = true;
}
[System.ComponentModel.DefaultValue(true)]
public override bool AutoSize
{
get
{
return base.AutoSize;
}
set
{
base.AutoSize = value;
}
}
}
As expected, the VB.NET version is similar:
Public Class SampleLabel
Inherits System.Windows.Forms.Label
Public Sub New()
Me.AutoSize = True
End Sub
<System.ComponentModel.DefaultValue(True)> _
Public Overrides Property AutoSize() As Boolean
Get
Return MyBase.AutoSize
End Get
Set(ByVal Value As Boolean)
MyBase.AutoSize = Value
End Set
End Property
End Class
Unfortunately, this is a bit of a detour, but in the current version of Visual Studio .NET, you can't get around this problem any other way.
Another detail that can be annoying is the icon that displays in the toolbox. In Visual FoxPro, subclasses automatically use the same icon as their parent class. For this reason, your label would automatically have a label icon in VFP, unless you specifically change it. In .NET, you have to assign toolbox icons to each class individually. Otherwise, a generic icon will display. You can assign a .NET toolbox icon in a number of ways. The most reliable way is to assign an attribute to the class that defines the icon the class will use when it displays in the toolbox. The attribute is called ToolboxBitmap, and it's defined in the System.Drawing namespace (be sure to include that namespace if you haven't done that already). You can use it to assign a specific bitmap file, or alternatively, to indicate you want to use the same bitmap as another class/type. Here's how to do this in C#:
[ToolboxBitmap(typeof(System.Windows.Forms.Label))]
public class SampleLabel : System.Windows.Forms.Label
{
// Rest of the class remains unchanged
}
Here's how to do it in VB.NET:
<ToolboxBitmap(GetType(System.Windows.Forms.Label))> _
Public Class SampleLabel
Inherits System.Windows.Forms.Label
' Rest of the class remains unchanged
End Class
Figure 3 shows the new label control in the Toolbox, with the appropriate image.
Of course, there's a lot more to say about subclassing individual controls, but from a Visual FoxPro point of view, you've now explored the main differences. We recommend using visual inheritance for individual controls, just like most developers recommend using it in Visual FoxPro. In fact, just like in Visual FoxPro, we recommend subclassing all visual controls, even if you have no intention of changing anything. Creating an additional layer of subclassing simply provides a great way to add generic functionality, or fix anomalies at a later point. It can be a true lifesaver!
Subclassing ASP.NET WebForm controls
.NET Windows Forms and Web Forms are similar in many ways, including how they handle individual controls . For this reason, it's possible to subclass ASP.NET controls the same way you subclass WinForms controls. Here's a C# example that demonstrates how to subclass an ASP.NET label:
public class SampleLabel : System.Web.UI.WebControls.Label
{
public SampleLabel()
{
}
}
The ASP.NET version is so similar we'll skip it. Also, adding the control to the toolbox works identically to the WinForms version.
Of course, you can subclass controls (both in WinForms and WebForms) for a number of reasons. One reason is to change intrinsic behavior, such as the auto-size property. Another reason is to add business functionality to a class. The following WebForms example shows how to create a drop-down list that contains a list of customers:
using System.Data.SqlClient;
public class CustomerList :
System.Web.UI.WebControls.DropDownList
{
private DataSet dsCustomers;
public CustomerList()
{
this.dsCustomers = new DataSet();
SqlConnection conLocal = new
SqlConnection("Initial Catalog=Northwind;" + _
"Data Source=localhost;User Id=devuser;password=devuser");
conLocal.Open();
SqlDataAdapter adCustomers = new
SqlDataAdapter("SELECT * FROM Customers", conLocal);
adCustomers.Fill(dsCustomers, "Customers");
conLocal.Close();
this.DataSource = dsCustomers.Tables[0];
this.DataTextField = "CompanyName";
this.DataBind();
}
}
Here's the VB.NET version:
Imports System.Data.SqlClient
Public Class CustomerList
Inherits DropDownList
Private dsCustomers As DataSet
Public Sub New()
Me.dsCustomers = New DataSet
Dim conLocal As New _
SqlConnection("Initial Catalog=Northwind;" + _
"Data Source=localhost;User Id=devuser;password=devuser")
conLocal.Open()
Dim adCustomers As New _
SqlDataAdapter("SELECT * FROM Customers", conLocal)
adCustomers.Fill(dsCustomers, "Customers")
conLocal.Close()
Me.DataSource = dsCustomers.Tables(0)
Me.DataTextField = "CompanyName"
Me.DataBind()
End Sub
End Class
Both versions create a drop-down list you can simply drop onto a form and, immediately, the drop-down list will be bound to customer information retrieved from the Northwind database. Note this example is somewhat simplified because it retrieved data directly from the database, rather than using a business object. But the overall concept remains the same, even in production environments.
Subclassing entire Windows forms
Just like in Visual FoxPro, .NET lets you subclass entire forms. In Visual FoxPro, you differentiate between forms (stored in .SCX files) and form classes (stored in .VCX files). Forms are instances of the VFP Form class, while form classes are subclasses of Form. SCX forms have the advantage of being instantiated easily. They also have other advantages. For example, SCX forms have data environments, whereas VCX forms don't. However, the advantage of form subclasses stored in .VCX files is that you can subclass them and therefore reuse them easily. SCX-based forms are rather inflexible and you can only use them as they are. This has lead to much confusion in VFP, and has been the source of many discussions regarding which way to go.
.NET doesn't differentiate between form instances and form classes. Instead, every form created in VS.NET is a new class, derived from the default Form class (or another class ultimately derived from a default form). You can instantiate the new form class as is, or you can subclass it to refine and reuse it further.
You can easily create a new form class by creating a Windows Forms project (either in VB.NET or C#). Right-click on your project in the Solution Explorer and select "Add / New Windows Form." Name the new form "GenericForm" if you want to follow along with the example code. Drop two buttons on the form and change their caption (Text property) to OK and Cancel. Align the button close to the bottom right edge of the screen, and use the Anchor property (set to "Bottom, Right") to make sure the buttons stay close to that edge, even if the form gets resized. The result will be a form similar to the one in figure 4. You can also get into the nitty-gritty details of the form class and change things such as how the form's background color is rendered by adding this code to the Paint event (VB.NET):
Private Sub GenericForm_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
Me.ResizeRedraw = True
Dim oBrush As New LinearGradientBrush(Me.ClientRectangle, _
Color.White, Color.Red, 45)
e.Graphics.FillRectangle(oBrush, Me.ClientRectangle)
oBrush.Dispose()
End Sub
Here's the C# version:
private void GenericForm_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
this.ResizeRedraw = true;
LinearGradientBrush oBrush = new
LinearGradientBrush(this.ClientRectangle,
Color.White, COlor.Red, 45);
e.Graphics.FillRectangle(oBrush,this.ClientRectangle);
oBrush.Dispose();
}
Of course, the details of what you want to do with your form class are up to you. This code is only an example.
At this point, you could use your new form by simply instantiating it, but of course, you're more interested in creating subclasses. To do that, you first have to compile your project. Then select "Add / New Inherited Form." This launches a dialog that displays all form classes in the current project. You want to pick your GenericForm class. This creates the subclass and displays it in design mode (figure 5).
This is similar to how subclassing forms works in Visual FoxPro. However, there are a few key differences worth pointing out here. For one, note the little icon in the top-left corner of each button. The icon indicates this button instance has been defined in a parent class, which is a nice usability feature.
Just like in Visual FoxPro, you can select a button. However, in .NET, you can't manipulate the button. The property sheet appears disabled, and you can't move or modify the button in any other way. Initially, VFP developers often see this as a limitation, but in fact, it's a great feature! The reason you can't manipulate the buttons is they're set to be "private" or "friend" (depending on the language) in the GenericForm-class. "Private" is .NET's equivalent of VFP's "hidden." This means the button can only be manipulated from within the class in which the button is defined. The buttons will still show up in subclasses, but you can't modify them in design mode or through runtime code. You can easily fix this problem by setting the visibility of the control to either "public" or "protected" in the parent class.
Having this mechanism available on controls is powerful and also makes a lot of sense. .NET simply treats contained controls as "members" of the class, similar to properties being members of the class. For this reason, you can set the visibility on those controls the same way you can set visibility on properties and methods. This creates a consistent model throughout .NET. It also provides a great deal of control, letting you specify exactly which members of a form can be changed in subclasses, and which can't.
Another aspect that's different in .NET is that all user-defined code is executed, even at design time. You may have noticed the code that creates the gradient background caused the form to look different in the designer (figure 5). In Visual FoxPro, user-defined code won't run when a subclass is in design mode. Visual FoxPro strictly differentiates between code written by the developer (in VFP), and code that comes from the VFP base classes (written in C++). Code written by the VFP developer only executes at runtime. .NET, on the other hand, doesn't differentiate between code Microsoft wrote, and code the developer writes. For this reason, all the code you put into the parent class becomes part of the class (no ifs, ands, or buts!). There's no technical difference between the code you write to create the gradient background, and code Microsoft wrote to make the form's background gray. You simply changed the appearance of the class, and all subclasses will take on that appearance, even during design time.
Having the ability to execute user-defined code during design time is great. It provides a better and more productive design time experience. However, it can also have unexpected side effects. For example, if you added code to the Paint event that opens a connection to the database or writes to a log file, that code would also execute during design time. This is something to be aware of.
Note: Each form has a DesignMode property. This property is set to True during design time, which lets you add logic that runs depending on whether the form instantiates in the designer. Code that performs operations such as database access should probably only execute when DesignMode is False.
WinForms user controls
Developers usually don't want to subclass entire forms. Instead, they want to define "partial interfaces" by creating a container that holds a number of controls. You can then drop this container on a form among other controls. In Visual FoxPro, the control you use for such an operation is called Container. In VS.NET, a similar approach is available via "user controls." Figure 6 shows a user control we use to edit contact or name information on a form.
In many ways, user controls work just like forms. You can subclass them the same way, control visibility works the same way, etc. You can easily drop a user control on a form. Unfortunately, the designer doesn't handle user controls as well as in Visual FoxPro. The major limitation is that it isn't possible to manipulate controls within the instantiated user control through the designer, even if the controls are set to public. This is simply a designer shortcoming, not an issue with the underlying object model. In fact, all the controls are available and you can manipulate them through source code. Hopefully, a future version of VS.NET will provide better designer support in this area.
Subclassing ASP.NET Web Forms
ASP.NET Web Forms work a little differently than Windows Forms. Windows Forms are subclasses of the System.Windows.Forms.Form class, and all modifications of that class are stored in source code files. ASP.NET subclasses all Web Forms from the System.Web.UI.Page class. This class then contains all code for events and methods, but there's also a secondary file that defines which objects are on the page and where they're located. You can do this with .aspx files, which contain a mixture of declarative instance definitions and HTML code. For example, instead of instantiating a new text box using a "new" operator, the text box is defined using this declarative markup snippet:
<asp:TextBox id="TextBox1" style="LEFT: 60px; TOP: 65px" runat="server"></asp:TextBox>
This is convenient in many ways because declarative instantiation of objects adds a lot of flexibility to Web Forms. (In future versions of Windows, especially Windows Longhorn, declarative programming will be available for Windows development as well). However, it also means that whenever you subclass the page class, all you'll subclass is the code that exists in the source code file, but the declarative instantiation of objects is missing. For this reason, subclassing in ASP.NET Web pages is mostly confined to the page class itself, not its members.
Note that it's possible to define all member objects programmatically, even in ASP.NET. However, the designer uses the declarative approach, making it tedious to go the programmatic route. Also, mixing HTML with instantiated controls further complicates the matter. Another approach would be to subclass a page and create a new .aspx file that defines instances of the same name. This works, but is difficult to maintain. Declarative programming has many advantages, but so far, it doesn't mix well with inheritance and therefore often represents the end of the inheritance chain. It remains to be seen how this will evolve in future versions of ASP.NET.
Leverage your VFP expertise
Overall, visual inheritance works well in VS.NET. There are many details that represent clear improvements over the Visual FoxPro inheritance model. On the other hand, there are some shortcomings with some of the designers, which currently make some things a bit harder than they are in Visual FoxPro. Visual FoxPro developers have a lot of expertise with visual inheritance, and most of that expertise translates well into .NET. During the last 10 years, Visual FoxPro developers have learned to architect sophisticated inheritance chains, and especially from a design point of view, things work similarly in .NET. Form inheritance trees work the same way, and we still recommend you subclass all UI controls before you first use them.
As always, if you have any questions, feel free to e-mail us.
By Claudio Lassala and Markus Egger