Export (0) Print
Expand All

A Crash Course on ASP.NET Control Development: Design-Time Features and Capabilities

 

Dino Esposito
Solid Quality Learning

April 2006

Applies to:
   ASP.NET 2.0
   Control development

Summary: In this article, Dino Esposito will first build a simple composite control and then decorate it with a few tailor-made attributes to provide its users a pleasantly rich design experience. (20 printed pages)

Click here to download the code sample for this article.

Contents

Introduction
A Sample Composite Control
Use Attributes to Talk to the Designer
Class-Level Attributes
Type Converters
Building a Custom Designer
Region Highlighting
Generating Design-Time Markup
Action Lists and AutoFormats
Putting It All Together

Introduction

An ASP.NET control has two distinct sets of functionality for when it is executed at run-time inside a page or used at design-time inside a host designer. Run-time capabilities determine, based on configuration, the markup that is output by the control. The design-time capabilities, instead, benefit of a visual designer such as Microsoft Visual Studio 2005. Design-time capabilities let the page author configure the control for run-time in a declarative and WYSIWYG (what-you-see-is-what-you-get) manner.

All standard ASP.NET controls provide a minimum set of design-time capabilities provided by base classes such as Control or DataBoundControl. The base design-time feature set is significantly richer in ASP.NET 2.0 than in previous versions, and allows for a tighter integration and interaction between controls and the host. Custom controls can be architected to integrate with the host designer just like standard and built-in controls. In this article, I'll first build a simple composite control and then decorate it with a few tailor-made attributes to provide its users a pleasantly rich design experience.

A Sample Composite Control

For the purposes of this article, let's architect a composite control that contains two main regions. The idea is to build a labeled text box—that is, a control made of a Label and a TextBox control combined together to provide an edit field with a descriptive text. The LabelBox control inherits from CompositeControl and exposes the properties in Table 1.

Table 1. Properties of the LabelBox control

PropertyDescription
HeaderGets a reference to the internal Label control.
HeaderTextGets and sets the text for the internal Label control.
LabelBoxSettingsGets an object that groups a few visual properties for the Label.
ModeGets and sets the display mode of the label.
Text Gets and sets the text for the internal TextBox control.
TextBoxGets a reference to the internal TextBox control.
TextBoxSettingsGets an object that groups a few visual properties for the TextBox.

The Mode property is declared of type LabelBoxMode—an enumerated type that counts the following values: Left, Top, Right, and Bottom. The value of the Mode property defines the position of the label with respect to the text box. By default, the label is displayed on the left.

Being a composite control, LabelBox overrides the CreateChildControls method to built a made-to-measure control tree. The code below gives an idea of the final layout:

protected override void CreateChildControls()
{
    base.CreateChildControls();
    Controls.Clear();
    CreateControlHierarchy();
    ClearChildViewState();
} 
protected virtual void CreateControlHierarchy()
{
   // Build the outermost container table
   Table outer = new Table();

   // Populate the table based on the display mode
   bool vertMode = ((int)Mode) > ((int)LabelBoxMode.Right);
   if (vertMode)
       CreateTwoRowTable(outer);
   else
       CreateTwoCellTable(outer);

   // Save the control tree
   Controls.Add(outer);
}

The resulting structure of the control markup is an HTML table with either two rows or two cells, depending on the position of the label. If the label should go either at the top or bottom of the text box, the resulting table will have two rows; otherwise, the table will count one row and two cells.

When devising a composite control, you generally think of it in terms of constituent regions. However, you rarely keep track of the constituent regions in code. In other words, you rarely define internal properties to reference the control tree that represents a given region. You track regions if you have a relatively sophisticated control layout and the need to access those regions frequently in events and postbacks.

The LabelBox control counts two logical regions—the header and edit region. The header region is represented by the table cell that contains the Label control; the edit region is represented by the table cell that contains the TextBox control. For such a simple control, you typically won't save regions to internal members. However, you should do that if you plan to build rich design-time capabilities. I'll return on the importance of regions later on in the article; for now, suffice it to say that the LabelBox control has two members flagged as internal and defined as below:

internal LabelBoxContainer _headerContainer;
internal LabelBoxContainer _textBoxContainer;

The LabelBoxContainer class is a helper class that simply renames TableCell. In other and richer scenarios, a similar class will enhance the feature set of the simple TableCell class.

public class LabelBoxContainer : TableCell
{
}

Two instances of LabelBoxContainer are created to contain the control tree for the header and edit region respectively. Each instance is saved in the corresponding internal member.

This trait is the only aspect of LabelBox that is worth some remarks. Figure 1 shows the LabelBox control in action in the Visual Studio 2005 environment.

Aa479051.cccdesign01(en-us,MSDN.10).gif

Figure 1. The LabelBox control

As you can see, the control behaves quite well in the host designer. It provides a faithful preview and refreshes automatically as you change property values in the Properties window. Could you hope for more? Well...actually yes. There are so many design-time features available, especially in ASP.NET 2.0, that missing any is bad.

Any reasonable strategy aimed at building a rich design-time behavior for ASP.NET controls passes through various steps:

  • Attributes to provide information and guidance to the host designer.
  • Type converters to simplify the editing of complex type properties.
  • Custom designers to take control of the rendering process of the control inside the designer.

In ASP.NET 2.0, a custom designer can do much more than in previous versions; in particular, it can now take control of the following features:

  • Auto-Format menu (this was also possible to some extent in ASP.NET 1.x).
  • Smart tags.
  • Region selection.

In the rest of the article, I'll walk you through the implementation of all of these features in the context of the LabelBox control.

Use Attributes to Talk to the Designer

Attributes are descriptive elements that optionally decorate types and members. Typically, an attribute exposes additional information about the type or member it decorates to any components that might be interested. Any piece of code can freely check the value of a given attribute and change its own behavior accordingly. Attributes don't change the behavior of the decorated component, but a decorated member might bring an external program to behave.

A control property flagged with, say, the Description attribute runs as usual with no run-time drawbacks. However, when the same control is hosted in Visual Studio 2005 IDE, the string associated with the attribute shows up in the Properties window.

Table 2 lists the main design-time attributes a control author can rely on to drive the behavior of the Visual Studio 2005 designer.

Table 2. Principal design-time attributes for ASP.NET controls

AttributeDescription
Bindable Indicates whether and how a property can be bound to data.
Browsable Indicates whether a property or an event should be displayed in the Properties window.
Category Provides the name of the category in which a property or an event belongs. This allows for logical grouping of properties and events in the Properties window.
DefaultValue Used to indicate the default value for a control property. If the property doesn't have a simple type, a type converter is needed.
Description Provides a description of a property or an event. The text is displayed at the bottom of the Properties window when the user selects a property or event.
Editor Specifies the editor to use for modifying a property in a visual designer.
Themeable Indicates whether the property can be added to a control skin in a theme. Not supported in ASP.NET 1.x.
TypeConverter Indicates the type converter class to use to convert the type of the property to text.

As an example, let's consider the declaration of the Mode property in the LabelBox control. The code includes the most commonly used attributes:

[Category("Appearance")]
[DefaultValue(LabelBoxMode.Left)]
[Description("Gets and sets the display mode of the control")]
public LabelBoxMode Mode
{
    get
    {
        object o = ViewState["Mode"];
        if (o == null)
            return LabelBoxMode.Left;
        return (LabelBoxMode)o;
    }
    set
    {
        ViewState["Mode"] = value;
    }
}

The Category attribute indicates the category under which the property will be listed in case the Properties window view is categorized. The attribute is ignored if the view is alphabetical. The Description attribute sets the text displayed at the bottom of the window. (See Figure 2.)

Aa479051.cccdesign02(en-us,MSDN.10).gif

Figure 2. How attributes affect the LabelBox control at design-time

Looking at Figure 2, you might think that all property values in the grid are rendered in bold face. To be precise, values are rendered with bold characters only when their value differs from the property's design-time default value. The design-time default value of a property is set through the DefaultValue attribute.

Each property is assigned a run-time default value in the get accessor. For the Mode property, the run-time default value is LabelBoxMode.Left. Needless to say, run-time and design-time default values are different animals. The run-time default value indicates the value assigned to the property unless you explicitly set it to something else. The design-time default value indicates a touchstone against which the value entered in the Properties window is compared. If different, the current value is rendered with boldface; otherwise, regular characters are used.

By default all of a control's public properties show up in the Properties window. Note that public fields (that is, members without get/set methods) are never displayed instead. To prevent a property from appearing in the Property grid, you decorate it with the Browsable(false) attribute.

[Browsable(false)]
public TextBox TextBox
{
    get
    {
        if (_textBox == null)
            EnsureChildControls();
        return _textBox;
    }
}

Typically, you mark as not browsable all properties designed for programmatic access only. The LabelBox control, for example, sports a property that represents the internal TextBox control. This property is clearly designed for access through code and should not be made visible at design-time.

Class-Level Attributes

Once you've shipped a custom server control, users will typically consume it through the Visual Studio 2005 toolbox. To add a new control to the toolbox, a developer must locate the assembly and select the control. A few attributes apply at the control class to instruct Visual Studio 2005 in this situation. Attributes are listed in Table 3.

Table 3. Class-level attributes

PropertyDescription
DefaultEventIndicates the default event for the control. This is the event that is handled when users double-click the control in the designer. This attribute is valid only on the control class declaration.
DefaultPropertyIndicates the default property for the control. The default property is selected in the Properties window when users click the control in the designer. This attribute is valid only on the control class declaration.
ToolboxBitmapIndicates the name of the bitmap resource to be used as the control's bitmap in the Visual Studio 2005 toolbox. The bitmap must be included as a resource in the control's assembly.
ToolboxDataUsed to indicate the default markup that Visual Studio adds to the .aspx page when you drop the control onto the Web form. This attribute is valid only on the control class declaration.
ToolboxItemIndicates whether the class should be listed in the toolbox.

By default, all control classes in the assembly are added to the toolbox. If your assembly defines control classes not designed for use from the page (internal-use controls, for example) decorate them with the ToolboxItem(false) attribute to keep them off the toolbox.

Page authors usually drag controls from the toolbox to the Web form. When this happens, some markup is generated in the target page to reference the control. You can use the ToolboxData attribute to customize the initial HTML content that is placed in the designer when the control is dragged from the toolbox.

[ToolboxData("<{0}:LabelBox runat=\"server\" />")]
[DefaultProperty("Mode")]
public class LabelBox : CompositeControl
{
  :
}

The {0} placeholder represents the prefix used for the control. The DefaultProperty attribute indicates the property that is automatically selected in the property grid when the page author selects the control in the Web form. The DefaultEvent property, instead, indicates the event (if any) for which a handler is automatically created when the page author double-clicks on the control.

Type Converters

Most of the control properties are of a simple type, such as string, numbers, date, colors, enums, and similar. It is not uncommon, however, for certain properties to need to be rendered with a custom class. How would you edit the members of this class at design-time? And how would you persist the values of this class in the source page (at design-time) and in the viewstate (at run-time)? The answer is the same for both questions: type converters.

A type converter is an object that implements some logic to convert one type into another. Type converters come in handy when serializing the value of nonprimitive types (Font, for example) to a string format to be saved to an .aspx file. At the same time, type converters read a string description of a value and return it to its original type. Type converters are at work behind the scenes when you specify, for example, some font properties through text attributes at design time and retrieve a Font object ready to use at run time.

At design-time, type converters are usually employed to transform a complex type into a string, and vice versa. At run-time, they are used to persist and restore the same complex type to and from the viewstate. Using a type converter to serialize a complex type is more efficient than resorting to the binary formatter—the .NET last resource for serialization. Let's consider the following code fragment:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public LabelBoxSettings LabelSettings
{
    get
    {
        if (_headerSettings == null)
            _headerSettings = new LabelBoxSettings();
        return _headerSettings;
    }
}

The LabelSettings property groups together a few visual properties, which will be set on the internal Label control. Properties of a complex type are often edited in Visual Studio 2005 using an expand/collapse panel. Canonical examples are style and font properties. To obtain the same behavior from your custom type—LabelBoxSettings in this case—you are to define a type converter for it, and properly decorate the class:

[TypeConverter(typeof(LabelBoxSettingsConverter))]
public class LabelBoxSettings
{
   :
}

A type converter for an ASP.NET control property type generally derives from ExpandableObjectConverter, and is a class that overrides four methods:

  • CanConvertTo
  • CanConvertFrom
  • ConvertTo
  • ConvertFrom

In this particular context, the target type to convert to and from is string. In general, though, you can plan a type converter for any sort of type conversion you can think of. The standard implementation entails that ConvertTo builds a comma-separated array of strings in which each element corresponds to the string representation of a property. ConvertFrom, of course, will do the reverse.

For example, LabelBoxSettings features four members—BackColor, ForeColor, FontName, and FontSize. The string representation of these values form a comma-separated array that is the serialization format of the LabelSettings property. In Visual Studio 2005, you edit the property as shown in Figure 3.

Aa479051.cccdesign03(en-us,MSDN.10).gif

Figure 3. Editing a complex and custom type

As you can see, the various members of the LabelBoxSettings class can now be edited individually and will be persisted to the .aspx file using the format dictated by the DesignerSerializationVisibility attribute. The attribute can take any of the values listed below.

Table 4. Values for the DesignerSerializationVisibility Attribute

ValueDescription
ContentThis option causes the contents of the property (for example, LabelSettings) to be serialized as a whole. All sub-properties will be serialized using an ad hoc naming convention. For example, for the BackColor sub-property, an attribute named LabelSettings-BackColor will be created.
HiddenThis option hides the property from serialization. The property will simply not be persisted in the markup code of the control.
VisibleDefault value; causes the top-level property to be serialized. When this option is set, a property such as LabelSettings is serialized as a comma-separated list of child values. All sub-properties will not be saved individually but within a unique, all-encompassing attribute named LabelSettings.

When editing a complex property in Visual Studio 2005, make sure that changes to individual sub-properties are promptly notified to the parent and refresh the control in the designer. To provide for this, add the NotifyParentProperty to properties of the LabelBoxSettings class:

[TypeConverter(typeof(LabelBoxSettingsConverter))]
public class LabelBoxSettings
{
   [NotifyParentProperty(true)]
   public string FontName
   {
      get { return _fontName; }
      set { _fontName = value; }
   }
   :
}

For more details on the implementation of type converters, refer to the companion source code.

Building a Custom Designer

Most of what I've discussed so far is not specific to ASP.NET 2.0, but covering these topics is necessary to give a precise idea of the design-time machinery. The final step is also where most of the new ASP.NET 2.0 features fit—building custom designers for server controls.

When a control is dropped onto a Web form, Visual Studio 2005 queries the designer of the control to obtain a handful of markup code to provide page authors with a faithful preview of the page.

If you never heard about control designers, don't panic. All server controls have a control designer, since the very early days of ASP.NET. The default control designer is a class named ControlDesigner. This class works calls the Render method on the control and captures its output to a string that is then returned to Visual Studio 2005.

This behavior presents a few drawbacks in ASP.NET 1.x, but everything seems to work just fine (and better) in ASP.NET 2.0. If you reckon that this is good enough for your (custom) controls, feel free to jump to the conclusion of this article, Putting It All Together. Otherwise, read on and learn what you can do to make editing your ASP.NET 2.0 controls a pleasant experience.

Changes and improvements fall in three main areas:

  • AutoFormat menu
  • Smart Tags
  • Regions

To support these new features, you just build a designer class that overrides a few more methods and properties than required in ASP.NET 1.x.

Region Highlighting

If you design composite controls that have a user interface split in two or more distinct blocks, you can help your users by providing context-sensitive smart tags based on the region that is currently highlighted. Still a bit confused on what it really means? Take a look at Figure 4.

Aa479051.cccdesign04(en-us,MSDN.10).gif

Figure 4. Regions and context-sensitive smart-tags

Why is the "Customer" label rendered with a different background color? No, it's not because I set the background color accordingly. More simply, I first selected the control as a whole in the form, and then I clicked on the label region. If you now click on the text box (the second region defined on the LabelBox control), the background of the text box area will change. More importantly, the tasks also change to reflect the selected region.

How do you define design-time regions for a custom control? Before I get to this, let me briefly review what a designer class actually is.

A designer class is a class that inherits from a base class—CompositeControlDesigner in this case. The class needs to override a few methods, as in the table below:

Table 5. Overrides for a control designer

MemberDescription
CreateChildControlsDefines the regions.
InitializePrepares the designer to generate the control's markup.
GetDesignTimeHtmlReturns the markup to be shown in the Visual Studio 2005 IDE.
OnClickHandles user clicking on the control at design-time.
ActionListsProperty; lists the contents of the control smart tag.
AutoFormatsProperty; lists the predefined formats for the control.

In Initialize, you don't usually do much more than save a reference to the control instance that is used by Visual Studio 2005.

public class LabelBoxDesigner : CompositeControlDesigner
{
    private LabelBox _labelBoxControl;
    public override void Initialize(IComponent component)
    {
        _labelBoxControl = (LabelBox) component;
        base.Initialize(component);
    }
    :
}

In CreateChildControls, you define the regions you intend to use at design-time. A design-time region should match the contents of a run-time logical region. As mentioned, for the LabelBox control you have two logical regions—the header and edit regions. The header region corresponds to the Label control; the edit region corresponds to the text box. It doesn't have to be a 1:1 correspondence between regions and controls; more likely, you have a region that covers a control tree. At design-time, though, you establish a link between a region and the root of the region's control tree. Both regions correspond to all controls contained in a table cell, and the table cells are saved to internal members of the LabelBox control. You use these members to define regions in the designer.

protected override void CreateChildControls()
{
    base.CreateChildControls();
    if (_labelBoxControl._headerContainer != null)
    {
        _labelBoxControl._headerContainer.Attributes.Add(
            DesignerRegion.DesignerRegionAttributeName,
            PROP_HEADERREGION.ToString());
    }

    if (_labelBoxControl._textBoxContainer != null)
    {
        _labelBoxControl._textBoxContainer.Attributes.Add(
            DesignerRegion.DesignerRegionAttributeName,
            PROP_EDITREGION.ToString());
    }
}

To define a region, you simply add an attribute to the HTML element that contains the control tree you identify as the "region." It is important to note that this additional attribute is emitted at design-time only. The constants in the preceding code are just user-defined, progressive numbers to label regions. Using numbers instead of strings is arbitrary; what matters is that you find out a way to uniquely label regions.

The designer first calls CreateChildControls to ensure that the control tree is up and running, and next invokes GetDesignTimeHtml to grab the design-time markup for the control.

Generating Design-Time Markup

In ASP.NET 2.0, the GetDesignTimeHtml method of the base ControlDesigner class comes with two overloads.

public virtual string GetDesignTimeHtml()
public virtual string GetDesignTimeHtml(DesignerRegionCollection regions) 

The first overload just retrieves the markup used to represent the control at design time. In addition, the second overload also populates the specified collection with region information.

public override string GetDesignTimeHtml(DesignerRegionCollection regions)
{
    // Add regions 
    regions.Add(new DesignerRegion(this, HEADERREGION));
    regions.Add(new DesignerRegion(this, EDITREGION));

    // Set the highlights as needed
    if (_regionHighlights[PROP_HEADERREGION])
        regions[PROP_HEADERREGION].Highlight = true;
    if (_regionHighlights[PROP_EDITREGION])
        regions[PROP_EDITREGION].Highlight = true;

    return base.GetDesignTimeHtml(regions);
}

The constants used to define regions are plain user-defined constants that name each region. Once you have regions defined, you might also want the Visual Studio 2005 IDE to highlight the currently selected region. For this purpose, you set the Highlight property on the DesignerRegion class to true. What's the event that determines whether or not a region should be highlighted? This is mostly up to you, but there aren't many events you can catch from within the Visual Studio 2005 IDE. There are no events at all in ASP.NET 1.x, and only the OnClick event in ASP.NET 2.0. To support region highlighting, therefore, you also need to override the OnClick method.

protected override void OnClick(DesignerRegionMouseEventArgs e)
{
    base.OnClick(e);

    // Figure out which region needs to be highlighted
    if (e.Region != null)
    {
        // Highlight Header region
        if (e.Region.Name == HEADERREGION)
        {
            _regionHighlights[PROP_HEADERREGION] = 
                !_regionHighlights[PROP_HEADERREGION];
            _regionHighlights[PROP_EDITREGION] = false;
        }
        else
        {
            // Highlight Edit region
            if (e.Region.Name == EDITREGION)
            {
                _regionHighlights[PROP_EDITREGION] =
                    !_regionHighlights[PROP_EDITREGION];
                _regionHighlights[PROP_HEADERREGION] = false;
            }
        }

        // Re-render the control to show the highlight
        UpdateDesignTimeHtml();
    }
}

The _regionHighlights variable is a user-defined Boolean array used to track the highlight status of each region. Note that the instance of the designer class in which the variable is defined is active all the time the host page is up in the Visual Studio 2005 IDE—there's no Web request/response mechanism to invalidate local variables. When the user clicks inside the control area, the OnClick method is called and receives information regarding the region (if any) in which the click occurrs. The OnClick method updates internal information and calls UpdateDesignTimeHtml to re-render the control. In doing so, GetDesignTimeHtml is invoked to render the selected region with a different background color, as you see in Figure 4.

In this case, the design-time appearance of the server control is determined by the standard markup. By overriding the parameterless version of GetDesignTimeHtml, you can take full control of what's being rendered at design-time. (See Figure 5.)

Aa479051.cccdesign05(en-us,MSDN.10).gif

Figure 5. A fully customized design-time rendering for the LabelBox control

The grey box you see in the figure is identical to what some ASP.NET designers employ for their controls—for example, user controls.

Action Lists and AutoFormats

To take control of the action list and auto-format menu of the control, you override two properties on the designer class:

public override DesignerActionListCollection ActionLists
{
    get
    {
       DesignerActionListCollection list;
       list = new DesignerActionListCollection();
       list.AddRange(base.ActionLists);
       LabelBoxActionList lbList = new LabelBoxActionList(
             this, _regionHighlights[PROP_HEADERREGION])
       list.Add(lbList);
       return list;
    }
}

public override DesignerAutoFormatCollection AutoFormats
{
    get
    {
        DesignerAutoFormatCollection autoFormats;
        autoFormats = new DesignerAutoFormatCollection();
        autoFormats.Add(new BlueFormat());
        autoFormats.Add(new YellowFormat());
        autoFormats.Add(new GreenFormat());
        return autoFormats;
    }
}

The action-list refers to the contents of the window that shows up when the user clicks on the smart-tag button, as in Figure 4. The contents of the window come from a control-specific class that inherit from DesignerActionListCollection.

The action-list contains various types of items—header, property, and method. The header item generates a descriptive text such as "Orientation" in Figure 4. The property item outputs a control bound to a variable. The control depends on the type of the property, and is a checkbox for a Boolean value or a drop-down for an enum value. The property typically matches a similar property on the control. For example:

public LabelBoxMode Mode
{
    get {return _labelBoxControl.Mode;} 
    set {
      PropertyDescriptor pd;
      pd = TypeDescriptor.GetProperties(_labelBoxControl)["Mode"];
      if (pd != null)
          pd.SetValue(_labelBoxControl, value);
    }
}

The Mode property on the LabelBoxActionList class gets and sets the value of the Mode property on the LabelBox control through the smart-tag window. Note that you should use reflection to set the value of the property to enable the Visual Studio 2005 IDE to fully support menu updates such as undo and others.

Finally, the method item adds a clickable element to the window and the specified method executes in response. For example, the "Set Display Mode" link of Figure 4 rotates the position of the label.

In the constructor of the custom action-list class, you can pass any information you deem essential to make the action-list region specific. Figure 6 shows the tasks window when the Header and the Edit region is selected. (The change is limited to the bottom text box.)

Aa479051.cccdesign06(en-us,MSDN.10).gif

Figure 6. Region-sensitive action lists

The AutoFormat command appears automatically if the designer class overrides the AutoFormats property. You populate the AutoFormats collection property with instances of classes derived from the base DesignerAutoFormat class.

public class BlueFormat : DesignerAutoFormat
{
    public BlueFormat() : base("(Blue-ish formatting)")
    {
    }
    public override void Apply(System.Web.UI.Control control)
    {
        LabelBox _control = control as LabelBox;
        if (_control != null)
        {
            _control.LabelSettings.BackColor  = Color.Transparent;
            _control.LabelSettings.ForeColor = Color.Blue;
            _control.TextBoxSettings.BackColor = Color.Cyan;
            _control.TextBoxSettings.ForeColor = Color.Blue;
        }
    }
}

A designer AutoFormat class simply overrides the Apply method to set some visual properties to predefined values.

Aa479051.cccdesign07(en-us,MSDN.10).gif

Figure 7. A custom AutoFormat window

By clicking the Apply or OK button, the settings defined in the Apply method will be passed to the control hosted in the Visual Studio 2005 designer and then persisted to the .aspx source file.

Putting It All Together

How can you bind a designer to a control class? Couldn't really guess it? You use an attribute, precisely the Designer attribute.

[Designer(typeof(LabelBoxDesigner))]
public class LabelBox : CompositeControl
{
   :
}

Without such an attribute, the control will inherit the designer of its base class. When you create a custom control and get to create a custom designer, it is important that you choose the designer of its base class as your starting point. In ASP.NET 2.0, a lot of designer classes exist so that you can choose the one that best fits your new control.

Building a rich and pleasant design-time experience is not as hard a task as it sometimes was in ASP.NET 1.x. So there are no more excuses if you're writing controls that other developers will have a chance to use.

 

About the author

Dino Esposito is a Solid Quality Learning mentor and the author of Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy, Dino is a frequent speaker at industry events worldwide. Get in touch at cutting@microsoft.com or join the blog at http://weblogs.asp.net/despos.

Show:
© 2014 Microsoft