Build a Control Designer

Improve your users’ design-time experience.

Antoine Victor

October 30, 2009

10 Min Read
ITPro Today logo

asp:feature

LANGUAGES: C#

TECHNOLOGIES: Custom Controls

 

Build a Control Designer

Improve your users' design-time experience.

 

By Antoine Victor and Doug Seven

 

With a well-implemented design-time experience, you cangive developers a better idea of how their current property settings willaffect the appearance of your controls at run time. Enabling design-timesupport for your ASP.NET server controls might be exactly what you need toencourage developers to continue using them.

 

In this article, you will learn how to use theControlDesigner class and the GetDesignTimeHtml method to build a design-timepresentation for your control. You will learn when you can rely on the defaultdesign-time support provided by the .NET Framework and when you need to get in thereand do it yourself. Throughout this article, we will demonstrate thecapabilities we are describing with a Featured control. This control uses theRandom class to render a randomly selected product or other data stored in theDataTable set to the DataSource property. You can use this type of control ine-commerce applications, or you can alter the data source and use it as aFeatured articles control. The DataTable can be cached and used to display adifferent featured product on each refresh of the page.

 

Use the ControlDesigner Class

The ControlDesigner class implements a few useful methods,some of which are listed in Figure 1. If you build a new control and don'tbother to create a custom control designer, it's not the end of the world. Yourcontrol will still work; it simply will use the default designer. The defaultsimply displays the name of the class and the name of the instance of the class(the ID property of the control). In most cases, that's not what you want. Whatyou really want when working with a control in the Visual Studio .NET IDE is awhat-you-see-is-what-you-get (WYSIWYG) view of your control and the page it'son. Well, you get what you pay for, and the coding time you spend using thedefault designer (none) will deliver exactly the appropriate return: nothing.

 

Property Name

Description

Component

A reference to the control the designer is designing

 

Method Name

Description

GetEmptyDesignTimeHtml

Displays the control's class name and instance name

GetDesignTimeHtml

Displays the output from the render method unless overridden

Figure 1. These are the ControlDesigner class'sproperties and methods used in this example. This is by no means a completelist, but you can download one in this article's sample code.

 

Choose Your Method

The issue here really is composition vs. rendering. Compositionis a straightforward method for building controls; you simply write thecode to create instances of existing ASP.NET server controls and any propertysettings and method calls you require. Because the composition method involvescreating child controls, the default designer is unable to render the controlat design time. As mentioned earlier, you get what you pay for - because it isrelatively easy to build, it doesn't perform quite as well as a control whoserendering logic is custom built.

 

At this point, it might sound as if rendering is the wayto go because the performance will be better than with composition, but there'smore to the story. Rendering requires that all rendering logic be builtby the control developer. This means if your custom control requires aDataGrid, you must code the rendering logic and handle state management andpostback events. Controls that display simple HTML output by way of the rendermethod can, however, take advantage of the default designer because it candisplay simple output from the render method. So, what's the verdict?

 

If a server control requires child controls, you must usethe CreateChildControls method. If you are using the CreateChildControls methodof the WebControl class to build your control's user interface, the defaultcontrol designer won't do it for you. If you want to display something thatlooks more like your control will look at run time, you'll have to build acustom control designer.

 

If the server control requires only simple HTML and nochild controls, use the render method. The default designer will take care ofthe rest.

 

Figure 2 shows a control using the default designer. Thecontrol itself is the starter control Microsoft provides when you start a newWeb control project.

 


Figure 2. This is how the starter control appears in the Visual Studio.NET IDE when using the default designer. Instance 1 has an empty Textproperty. Instance 2 has its Text property set to "Text Property."

 

The default designer can display the value from thecontrol's Text property because the control uses the render method to outputthe control's user interface to the page. The code that renders the control'suser interface is only one line:

 

Protected Overrides Sub Render

  (ByVal output AsHtmlTextWriter)

        output.Write([Text])

End Sub

 

The default designer has no problem displaying the outputfrom the render method because it's simply a TextWriter; there are no specialinstructions or child controls to create. With a more complex control, such asthe Featured control shown in Figure 3 in which child controls are createdbased on values returned from a database, you need a custom designer to writethe code necessary to display the control properly at design time.

 


Figure 3. Here's the Featured control in the Visual Studio .NET IDE withcontrol properties set.

 

The Featured control displays products or items of yourchoice from the DataTable passed to it. In the examples shown here, the datacomes from a Products table in a SQL Server database. The products are selectedbased on the value in the Featured field. If a product is listed as a featuredproduct, it is displayed randomly in the control along with other featuredproducts.

 

The Featured class has a DataSource property used toselect the featured products. The Featured class also has properties fordetails about the featured item, including itemName, itemImage,itemDescription, and itemPrice.

 

Although the control itself can be useful in sites requiredto feature an article, product, or other item, the control is not the star ofthe show. The Featured control would be difficult to use in developing aproduction site where the layout of products and other items on the page isessential. Without a custom designer, determining how much space the control'scontent would require at run time would be impossible, and that would prohibitcontrol and content alignment as well. To solve this problem, a custom designeris used to make the control appear in the IDE at design time exactly how itwill appear at run time. In this way, layout is done by aligning the elementsof the page visually as they appear on the screen, instead of the more typicalmethod of guessing, aligning, compiling, viewing, and then - oops! - aligning,compiling, and viewing again.

 

In Figure 4, the GetDesignTimeHtml method is used tocreate child controls based on property settings. In the GetDesignTimeHtmlmethod, the code from the CreateChildControls method is duplicated. But whenthe GetDesignTime method is called and the control has been bound to a datasource, default property values are used to display the control in thedesign-time environment for layout purposes. When the control is used todisplay a single product as defined by the property settings (itemName,ItemPrice, and so on), the property settings are displayed in the design-timeenvironment. When the custom designer is bound, the control appears in theVisual Studio .NET IDE the same way it would appear in the user's browser. Thisis helpful for layout tasks. Having a control display only the class andinstance names in the IDE makes proper layout almost impossible. Imagine youhave a control that takes about five lines to display, but, in the IDE, it onlyappears to take one line to display what I like to call "the green carrot ofdeath," also known as the control handle.

 

public override string GetDesignTimeHtml()

{

      

  StringWriter sw = newStringWriter();

  HtmlTextWriter tw = newHtmlTextWriter(sw);

 

  Featured ctlFeatured = (Featured) Component;

  

  Table MyTable = newTable();

  MyTable.CellPadding = 3;

 

  TableRowrowProductName   = new TableRow();

  TableRowrowProductDetails   = new TableRow();

  TableCell celImage   = new TableCell();

  TableCellcelProductImage  = new TableCell();

  TableCellcelProductName  = new TableCell();

  TableCellcelProductDescription   = newTableCell();

  TableCellcelProductPrice   = new TableCell();

 

  celProductName.Wrap =false;

  celProductName.ColumnSpan = 3;

  celProductName.Text ="" +

          ctlFeatured.itemName +

          "";

  rowProductName.Cells.Add(celProductName);

  MyTable.Rows.AddAt(0,rowProductName);

 

  Image imgProduct = new Image();

 

  imgProduct.ImageUrl =ctlFeatured.itemImageURL;

 

  celProductImage.CssClass= "sectionhead";

  celProductImage.Wrap =false;

  celProductImage.HorizontalAlign = HorizontalAlign.Center;

  celProductImage.Controls.Add(imgProduct);

  rowProductDetails.Cells.Add(celProductImage);

 

  celProductDescription.CssClass = "sectionhead";

  celProductDescription.Wrap = false;

  celProductDescription.HorizontalAlign =

          HorizontalAlign.Center;

  celProductDescription.Text = ctlFeatured.itemDescription;

  rowProductDetails.Cells.Add(celProductDescription);

 

  celProductPrice.CssClass= "sectionhead";

  celProductPrice.Wrap =false;

  celProductPrice.HorizontalAlign = HorizontalAlign.Center;

  celProductPrice.Text =ctlFeatured.itemPrice;

  rowProductDetails.Cells.Add(celProductPrice);

 

  MyTable.Rows.Add(rowProductDetails);

  MyTable.BorderWidth =Unit.Pixel(1);

 

  //add table to control

  MyTable.RenderControl(tw);

  return sw.ToString();

  }

Figure 4. TheGetDesignTimeHtml method from the Featured control is where the magic happens.

 

The control handle is the standard visual representationof a server control that uses the CreateChildControls method and does notinclude a custom designer. Although the control handle always is visible in theupper-left corner of all controls, it is the only thing that appears on acustom control with child controls and no designer. As you can see in Figure 5,doing layout with nothing to guide you but the position of the control handleis difficult, if not impossible. Any layout you do based on the control handlethat appears in the IDE will be completely invalid when the control handlechanges to five lines at design time - things won't line up, to say the least!When you're under pressure to meet a tight deadline on a project, the lastthing you need is to have layout issues because the controls you're usingaren't WYSIWYG.

 


Figure 5. Visual Studio .NET displays the "green carrot of death" when acontrol uses the CreateChildControls method and provides no custom designer.

 

Once you've created a control designer for your customserver control, there is one final task. After creating your designer, you mustbind it to your control. You do this by using an attribute of your controlclass. As you probably can imagine, the Designer attribute is used to bind thedesigner to the control class. This is the code to add the Designer attributeto your control class:

 

[Designer("CustomControls.Designer, CustomControls"),

DefaultProperty("itemName"),

ToolboxData("<{0}:Featuredrunat=server>")]

public class Featured :

  System.Web.UI.WebControls.WebControl

 

In this example, CustomControls.Designer is thecontrol-designer class named Designer, and CustomControls is the assembly inwhich the Designer class is defined.

 

Once the designer is bound to the control class, changesto the control's properties appear dynamically in the Visual Studio .NET IDE.This approach allows page layout to be done by visually placing controls in theproper location as opposed to guessing where they might appear once theycontain data.

 

In summary, when your custom server control requires theuse of child controls such as a TextBox or DataGrid, you must build a customcontrol designer to display the control's output at design time. The controldesigner must override the ControlDesigner class's GetDesignTimeHtml method.After creating the designer, you must bind it to the control class. Then, youmay use your control like any other control in the toolbox. Ah, the toolbox.Now, there arises another issue: Before you can use your control like anyother, you must add your control to the toolbox! Follow these steps: SelectCustomize Toolbox from the Tools menu; select the .NET Framework tab; click onthe Browse button; navigate to the folder where your control .dll lives (VB.NetBin C# BinDebug); select the .dll and click on Open; and click on OK.

 

Almost as if by magic, your control appears in thetoolbox. Later, in client applications that use your control, you can selectyour custom control from the toolbox as you would any other control, and areference to the .dll that contains the control definition will be added toyour project automatically.

 

The sample code in thisarticle is available for download.

 

Doug Seven is a senior .NET developer forAtomic Consulting Group Inc. with several .NET books to his credit and a fewmore on the way. Seven is a co-founder of the .NET online resourceDotNetJunkies.com, and he has worked with a variety of clients, from start-upcompanies to Fortune 500 companies, developing everything from desktop- andWeb-based applications to bleeding-edge, proof-of-concept applications usingmobile devices. E-mail Doug at mailto:[email protected].

 

Antoine Victor is a senior software developer with morethan 10 years of experience writing applications in various programminglanguages, including VB .NET and C#.  Antoine has developed document-management systems, e-commerce Webapplications, inventory- and order-tracking systems, and disaster-recoveryplans. Antoine currently focuses on high-level consulting projects andspreading the .NET gospel, and he is a co-founder of Professional DataManagement. E-mail Victor at mailto:[email protected].

 

Tell us what you think! Please sendany comments about this article to [email protected] include the article title and author.

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.