The Custom FormField Web Control
Since this article is not a tutorial on Web Control creation, I will skip to the part where I have created this control already (indeed I have) and tell you what this "FormField" control, as I will hereafter refer to it, does. My FormField control displays a field caption and a field textbox. It allows the optional display of a required indicator based on a property setting and even allows the text and style for this indicator label to be customized. In most cases, I can see this being a simple asterisk but you may want to make it a string of some kind. The control offers separate style properties for the label and the textbox to allow for maximum visual versatility. In fact, you can even turn off the label by setting a property, and you can position the asterisk above the textbox as well as to the left of it.
By giving the FormField control all this functionality, any WebForms can use the control as they see fit by simply setting some properties. I've programmed the code that provides all this "declaratively" at the Web Control level. Now let me restate a definition from above: the FormField control defines how
to do all sorts of things, and the WebForm decides what
needs to be done.
|Figure 4. Base and Derived Form Controls: This is an example of a base control that builds the infrastructure for a data-entry form along with a possible data-entry form as a derived control. Each control is responsible for its own visual elements only, making for easier manageability.|
Let me point out one other cool feature I have added to this FormField control. A ReadOnly
property on my custom Web Control not only sets the built-in ReadOnly
property on the internal textbox, but it also overrides the style on the textbox with one from an additional property I provided called ViewModeStyle
. This lets the WebForms that use my control set the field to a read-only state that can, at the same time, change the way the field looks, thus visually indicating its change of mode.
I said earlier that this would be the first step towards making this form conform more to the style of declarative programming by taking advantage of Web Controls. Indeed I have taken this first step and this control may prove quite useful in many other forms as well, but now I'm going to take things a little further.
When I designed the site I am pulling this example from, I had already developed the custom FormField Web Control so using it was a no-brainer. However, I anticipated a couple of more facts about my application. I knew it would have several data-entry forms throughout the application that would behave and look relatively the same. Some items with similar behavior and appearance include headings, a form submission button, and the necessity to display the entire form in an edit mode or a view-only mode.
I first created a custom Web Control that contained a heading for the data-entry form, a placeholder control to designate where to house the actual data-entry objects, and at the bottom of the control I placed a Submit button. Without getting into too many details, I also added several styling properties including one for the heading, the button, a field caption style, a field textbox style, and a viewmode field textbox style.
This class served as my base class for several other custom Web Controls that corresponded to each of my data-entry/edit forms. In the case of the form that started this example, I created another Web Control that inherited from the base class I just told you about and then added all the FormField controls that specifically made up my Name and Address data-entry form.
Allow me to explain a couple of technical details about this control. When you create a composite Web Control, you add its child controls to a Controls collection that's part of the Web Control class you are [eventually] inheriting from. This is the case in the base class I created above. But in the derived classes that actually contain the FormField controls, I added these controls not to the Controls collection of the custom Web Control but to the Controls collection of the PlaceHolder control I created in the base class. Remember that by inheriting from this base class you adopt all of its visual functionality including a PlaceHolder; then later you will fill that PlaceHolder in the derived controls (see Figure 4).
This technique allows a certain level of visual inheritance at the Web UI level. Other functionality I added to the form base class includes a ViewMode property that determines if the data-entry form was to be displayed in edit mode or view mode. Setting this property will change the ReadOnly properties of all my FormField controls accordingly. Remember previously when I described the FormField control, I mentioned that I designed it so that when the ReadOnly property is set to True, a different style was applied to the contained textbox. Since I included this functionality in this Web Control already, setting that property from the "form" control(s) changes the style on all the FormField controls contained.
I also want to mention sizing. One great advantage of dealing with objects in terms of separate Web Controls is that you size internal, or contained controls to the parent only; the parent being our custom Web Control. You typically size a contained control to 100% of the parent. What this means is that in the case of the FormField control, the textbox is sized to 100% of the parent minus the width of the label and the required indicator, if it is visible. This means that as you drag to resize the FormField control, the textbox will always resize accordingly. When many FormField controls are contained in your "form" Web Control, they are sized to 100% of their container, which may be an HTML table or the custom Web Control itself, or in case of the examples in this article, it is the PlaceHolder control that is contained in the base control.
|Figure 5. Two WebControl Views: These two Web Controls show a complete formtwo views of the same WebControl with a ViewMode property set to False, then True. Notice the lack of Submit button in the view-only form as well as the different style in the fields. What else do you notice different? (hint: State field). |
illustrates what the base form control and a derived form control would look like. Notice that the isolation of parts is very clear. The derived form control(s) need only worry about their specific controls and how they fit into their immediate container.
The final effect of all this is that you can drop a single control on a WebForm and you'll get the entire data-entry form, heading, and submit button included. You can resize this form as a single control and all contained controls will resize accordingly. This in itself is a big relief from having to size separate controls individually. By setting a simple ViewMode
property, you will see the entire data-entry form change style (see Figure 5
) and the submit button disappear.
Since my site required more data-entry/data-view forms, I created them as custom Web Controls that derived from my base class. I provided the base class and the form-specific control classes with many other properties to give it more versatility, most of which are outside the scope of this article, but I will mention that I found that sometimes I needed to reuse one of the data-entry forms in a couple of places with the difference of a couple of its fields being absent. In this case I simply added the ability to hide these fields and exposed it as a property of that control. I took care of the problem "declaratively" and the other WebForm that reused the data-entry control simply set a property to make the control appear differently.
It's important to mention that these "data entry" Web Controls were not the only thing residing on the WebForm(s), so you can image the code-clutter that I eliminated by taking this route. As far as business functionality was concerned, each of my data-entry Web Controls mapped to a business object which the control exposed as a property. The submit button that was created in the base control raised an event to the WebForm so in conjunction with the exposed business object, the appropriate database interaction can be performed. Full validation functionality may also be encapsulated within the control by implementing the IValidator interface and providing the appropriate validation code.