Developing Usable (.NET) Components
When building an application, developers often face the decision of either writing functionality themselves or look to a third party component to get the job done in a timely manner. In the case of the latter, a well-designed component can greatly enhance a developer’s productivity. If you write components, you have an obligation to ensure your components are well-designed, easy to deploy, and above all, easy to use.
Tutorial Details
- Technology: .NET (C#)
- Difficulty: Advanced
- Estimated Completion Time: 1 hour
The term “component” used throughout this article refers to a single reusable program
building block. While it is entirely possible to group multiple components together
to form one complex component, this article discusses the design of simple, single-purpose
components. This article focuses on, and uses, C# and .NET for examples, but you
can apply the same principles to any language and platform.
There are three aspects that component authors must address in order to write usable
components.
- Identifying the type of component
- Designing the API
- Documentation and Packaging
Identifying the Component’s Type
Identifying your component’s type is the crucial stepping off point of a writing
a usable component. It is impossible to write a clean and simple API, much less
package it for distribution, without adequately identifying its type. Thankfully,
this first step is easy.
The two primary component types are utility components to perform a specific, non-visual
task, and UI components (visual) to either enhance the user’s experience or make
development simpler for the developer. Consider the following component as an example:
public class HtmlRetriever { public static string GetPageSource(string url) { // code to get HTML from the specified URL // code to return the HTML } }
This is a simple component that only contains this HtmlRetriever class. This class
has a static method called GetPageSource() which accepts a string parameter containing
the URL of a page. This component contains no visual elements ñ it is simply a component
that performs some sort of function. As such, you can easily identify this component
as a utility component.
Now consider the next component, called LabelTextBox, that inherits UserControl
and contains markup in an ASCX file:
<asp:Label ID="lblLabel" runat="server" AssociatedControlID="txtTextBox" /> <asp:TextBox ID="txtTextBox" runat="server" />
The fact that this component inherits UserControl, directly incorporates ASP.NET
controls, and outputs HTML are obvious clues that this component is a visual component.
Therefore, it should be used and thought of as a control, the UI component of ASP.NET
WebForms.
As stated earlier, identification is the easiest step in writing a usable component,
but it has to be done before you can adequately start writing code. Follow these
rules of thumb when identifying a component’s type:
- If your component incorporates the use of, or is tied to, a UI element, your component
should itself be designed as a UI element for the platform you are developing for.
If your platform is ASP.NET WebForms, the component should be a control. If ASP.NET
MVC, it should be an HTML helper. If your platform is the browser (JavaScript),
the component should be designed to behave as an HTMLElement. - If your component simply works with data and is independent from the UI, it is better
served as a utility class or reference type.
Properly identifying the type of component you need to write determines how you
approach its API and packaging.
The API and Why it is Important
A well-designed API naturally leads to productivity due to its simplicity ñ developers
spend less time acquainting themselves with the API and more time writing code.
From this simplicity also comes maintainability, as designing your API with simplicity
in mind helps keep the developer’s code simple.
Several key factors play into a simple API, and you can achieve one by adhering
to the following guidelines:
- Follow naming conventions and general practices for your language and platform.
- Emulate an API from a similar component.
- Design for yourself.
- Design for flexibility and cleanliness.
Naming Conventions and General Practices
No matter what language you write in or platform you target, the most important
rule to follow when designing your API is to follow the naming conventions and general
practices specific to your language and platform. Doing so promotes uniformity and
thus simplicity.
In the realm of .NET, these conventions are centered on two types of casing:
- Pascal case ñ The first letter of an identifier is capitalized. If the identifier
consists of multiple words, then each word should start with a capital letter. Example:
MyClass is a proper name for a class. - Camel case ñ The first letter is lower-case, and any subsequent word in the
identifier begins with a capital letter. Example: myVariable.
Pascal case should be used for all class names, enumerations and their values, events,
interfaces, methods, namespaces, and properties.
Camel case should be used for all parameters and local variables.
Emulate Another API
When starting from scratch, it may be a good idea to look for components with a
similar utility and emulate their API if it makes since to do so. Consider text-based
ASP.NET controls as an example. The Label and TextBox controls’ primary function
is to present text to the user. As such, their default property is the Text property.
If you build another text-based control, it too should implement a Text property
to get and set the text-based content of the control.
The API for UI components are typically much easier to design and implement than
that of non-visual components. Your target platform’s UI components will more than
likely have a common set of properties and methods. For .NET components, this is
easily achieved by simply inheriting the System.Web.UI.Control , System.Web.UI.WebControls.WebControl,
or System.Web.UI.UserControl classes.
It is absolutely crucial that your UI component’s API matches that of other UI components
in your target platform. It promotes uniformity and familiarity for the developer
ñ cutting the amount of time needed to learn a new API.
Build an API for Yourself
Chances are you are building a component for your own needs, and you later decided
to release it to the world. After emulating the API of another utility or control,
the next step is to add the properties and methods you want to add. After all, you
build it for a specific purpose. Not only are you the first consumer of your own
component, you are also the best resource to supply it with an API. Remember to
keep it as simple and logical as possible. Also remember to provide your properties
and methods with descriptive and clear names.
Make it Flexible
When designing the API, be sure to make it flexible to handle multiple scenarios.
Use the API you built for yourself as a basis, and start planning for different
scenarios that other developers may want your component to handle.
Making HtmlRetriever Flexible
Consider the HtmlRetriever class from the previous section. Its GetPageSource()
method may work for your original intended purpose, but also assume consumers of
your class might want a page’s source code without element tags. Make it flexible
by refactoring the method into two overloads, like in the following code:
public class HtmlRetriever { // the first "default" overload public static string GetPageSource(string url) { return GetPageSource(url, false); } // the second overload public static string GetPageSource(string url, bool stripTags) { // code to get HTML source here if (stripTags) { // code to strip tags } // return data } }
You will notice quite a few changes from this iteration of the HtmlRetriever class
from what was presented in the previous section. As you read this code, notice how
the method names, and the names of their parameters, are descriptive. There is no
guesswork here; consumers of this class know exactly what the method does and how
the parameters change its behavior simply by how they are named.
The first overload serves as the default behavior of GetPageSource() ñ it returns
the HTML source code of a page with the tags intact. The second overload of GetPageSource()
is the workhorse of the GetPageSource() overloads. It accepts a URL and a Boolean
value as its parameters. It gets the HTML source and returns it with or without
the element tags depending on what value was passed to the stripTags parameter.
Overloading is a fantastic means of adding flexibility to your API. It also has
the added benefit of keeping your API clean and free of clutter.
Adding an API to LabelTextBox
Now refer back to the LabelTextBox. It inherits the System.Web.UI.UserControl class,
so the basis of the control’s API in place from the base class. LabelTextBox is
essentially a wrapper around a Label and TextBox, so some properties should be written
to wrap the underlying controls’ properties. The first is the Text property, which
wraps around the TextBox’s Text property:
public string Text { get { return txtTextBox.Text; } set { txtTextBox.Text = value; } }
The decision to make LabelTextBox’s Text property wrap that of the TextBox is a
design choice. The TextBox receives the most amount of attention from the user;
it therefore makes sense to make the Text property wrap around the TextBox’s Text
property. To set the Label control’s Text property, create a property called LabelText:
public string LabelText { get { return lblLabel.Text; } set { lblLabel.Text = value; } }
These two properties fit the primary need of this component ñ to get and set values.
However, a developer consuming this component may have use for a TextChanged event.
This can easily be done with the following code that wraps the TextBox’s TextChanged
event:
public event EventHandler TextChanged; protected void Page_Load(object sender, EventArgs e) { txtTextBox.TextChanged += new EventHandler(txtTextBox_TextChanged); } private void txtTextBox_TextChanged(object sender, EventArgs e) { if (TextChanged != null) { TextChanged(this, e); } }
The first line of this code specifies an EventHandler delegate called TextChanged,
allowing developers to add event handlers to handle the TextChanged event. Next,
in Page_Load(), the TextBox’s TextChanged event is handled by the txtTextBox_TextChanged()
method. This allows you to check if TextChanged has any subscribers, and if so,
execute those event handlers.
You could continue emulating the TextBox properties and methods for this control.
For the sake of brevity, this is as far as the API emulation will go in this article.
So now this component’s API consists of familiar members, such as the Text property
and the TextChanged event, as well as custom properties (LabelText) to fit your
needs. You could add more customizability, such as providing functionality to dynamically
add a validation control to validate the text within the TextBox. The possibilities,
and thus API, are limited only by your imagination. Just ensure that yourself, as
well as anyone else, has the appropriate tools to get the most out of your component.
Final Touches: Documentation and Packaging
How you document and package your component is just as crucial to its usability
as its API. All the hard work you put into your component is for naught if its documentation
is poor and it is difficult to add to a project. Follow the guidelines in this section,
and your component can easily be added to a project.
Use XML Comments
Chances are good that you rely on Intellisense when experimenting with pieces of
the .NET Framework you have never worked with before. The majority of developers
do the same thing. Intellisense is a fantastic tool, and your component can support
Intellisense if you use XML comments and generate an XML documentation file. The
following code adds an XML comment to the Text property of LabelTextBox:
/// <summary> /// Gets or sets the text contained within the text box. /// </summary> public string Text { get { return txtTextBox.Text; } set { txtTextBox.Text = value; } }
The triple-slash is not a typo. Simply typing the slash three times above a class,
property, or method automatically inserts the XML comments for that identifier.
The only information you need to provide is what that identifier does.
Follow these steps to enable XML documentation file generation:
- Open your project’s property pages.
- Click on the Build tab.
- Scroll down and check the box next to XML documentation file.
Non-Visual Component Distribution
Utility components are best distributed as class libraries. Doing so gives the developer
a quick and easy means to add your component to their project. All they have to
do is add a reference to the DLL to their project and they can begin using it.
It is also a feasible solution to release your component as code files, as long as
the code in the files pertains only to your component. Embedding the code within
a code-behind, or some other source file type, is unprofessional and causes consumers
of your component to copy and paste the code into separate code files.
The best of both worlds: release the DLL and the code files together. If you want
to sell your component, some marketplaces may require you to include the source
code. Providing a precompiled DLL is a bonus for developers since they can just
drop it into their project.
UI Component Distribution
Distributing UI components can follow one of two scenarios. The LabelTextBox component
discussed in this article is a UserControl (ASCX). Distributing this component as
an ASCX is fine. It gives developers the ability to easily augment the control to
fit their needs thanks to the markup portion of ASCX files.
The alternative is to derive from Control or WebControl, in which case you have
to manually code the control without using markup. For simple controls, like LabelTextBox,
this is not much of an issue. However, more complex controls require more code to
write the output HTML with Render() and RenderControl(). This approach makes distribution
much easier, as the only addition to a project is a DLL file (or a code file) as
opposed to the ASCX and its code-behind file.
Never, ever, ever release your code without packaging it.
Few things are more frustrating to a developer than having to copy and paste several
lines of code from an ASPX code-behind, or worse, an ASPX markup file. As a component
author, it is your responsibility to ensure consumers of your code can use your component
as quickly as possible. Anything more than adding a file or reference to a project
quickly detracts the developer from your component. Keep it simple!
Closing
Writing components is a noble undertaking. You spend (countless) hours writing code
to make another developer’s work easier. Take pride in writing usable components.
The more professional and usable you make them, the more developers will want to
use them.
Sell ASP.NET Scripts on CodeCanyon
Did you know that you can sell your ASP.NET scripts and components on CodeCanyon? Simply sign-up for a free author account, and start selling!
- Follow us on Twitter, or subscribe to the Nettuts+ RSS Feed for the best web development tutorials on the web.