- Adding Client-Side Behavior Using the ExtenderControlBase
- Adding Design-Time Support to Your Extender Control
- Adding Animations to Your Extender Control
- Summary
Adding Design-Time Support to Your Extender Control
The introduction of the Extender Wizard in Visual Studio 2008 has enhanced the design-time experience with regard to working with extender controls, and this section explains how to add design-time features of your own to give your controls that professional feel that users have become accustomed to.
Default Design-Time Experience
The ImageRotatorDesigner class shown in Listing 11.2 provides everything we need to get a basic design-time experience for our extender control. The ExenderControlBaseDesigner<T> that it inherits from makes it possible for the properties of our extender control to show up in the Properties window while the design-time focus is on the image control we are extending. Figure 11.4 shows the RotationInterval and ImageList properties that appear in the Properties window while the image control has focus in the designer. This default feature addresses one issue, which is being able to work with the ImageRotator properties in an integrated way, but still does not address the issue of data entry for the properties themselves and how that experience can be enhanced.
Figure 11.4 Extender properties on the image control
Adding Designers and Editors to Properties
In this section, we look at how to extend the design-time behavior of our ImageRotator ImageList property. The ImageList property that we worked with in Chapter 5 was rudimentary and prone to errors as a user entered in the values. In this version of the extender, we want to extend the functionality to support design-time editing and HTML source editing.
The road to these modifications requires a few steps as we add the functionality:
- Add attributes to the class.
- Add attributes to the property.
- Add editors to assist in assigning values.
- Create a type converter to support serialization.
Add Attributes to the Class
Most users expect when adding multiple entries to a control to be able to add them in the body of the HTML element. This is the experience we have when adding web service references or script references to the ScriptManager and one we want to have in our control.
The ParseChildren attribute enables us to add multiple entries inside our ImageRotator HTML tag and treat those entries as a single property assignment. By setting the ChildrenAsProperties property to true and the DefaultProperty to ImageList, as in Listing 11.6, we are effectively telling the designer that we want to have all the items contained in the body of our ImageRotator tag parsed and assigned to the ImageList property. The HTML fragment in Listing 11.7 shows what this looks like when the HTML editor is opened and the ImageRotator tag has entries.
Listing 11.6. ParseChildren Attribute Assignment
[ParseChildren(true, "ImageList")] ... public class ImageRotatorExtender : ExtenderControlBase { ... }
Listing 11.7. ImageList Assignment in HTML
... <asp:Image ID="BannerImage" runat="server" ImageUrl="~/images/1.jpg" /> <cc2:ImageRotatorExtender ID="BannerImage_ImageRotatorExtender" runat="server" Enabled="True" TargetControlID="BannerImage"> <cc2:ImageUrl Url="~/images/2.jpg" /> <cc2:ImageUrl Url="~/images/3.jpg" /> <cc2:ImageUrl Url="~/images/4.jpg" /> </cc2:ImageRotatorExtender> ...
Add Attributes to the Property
To fully implement the ability to add nested image entries to our ImageRotator extender, we need to add a couple of attributes, as shown in Listing 11.8, to our ImageList property, which provides hooks for the designer to integrate with our property and properly assign the image values.
The DesignerSerializationVisibility attribute is added to the property to ensure that the designer will serialize the contents of the property during design time. The setting of DesignerSerializationVisibility.Content instructs the designer to generate code for the contents of the tag and not the tag itself.
The PersistenceMode attribute is the other piece to this puzzle and is responsible for adding the <ImageUrl .. /> entries inside our ImageRotator tag as we add values to the property in the Properties window. The setting of PersistenceMode.InnerProperty specifies that the property is persisted as a nested tag inside the ImageRotator, as shown in Listing 11.7.
Listing 11.8. Designer-Specific Attributes for the ImageRotatorExtender Class
[ParseChildren(true, "ImageList")] ... public class ImageRotatorExtender : ExtenderControlBase { ... [DesignerSerializationVisibility( DesignerSerializationVisibility.Content)] [PersistenceMode(PersistenceMode.InnerDefaultProperty)] public ImageUrlList ImageList { ... } }
Add Editors to Assist in Assigning Values
The use of editors in your extenders can greatly enhance the user experience during design time and in some cases can lead to more accurate entry of data. Recall from Chapter 5 that we entered images to the ImageList property by adding URL entries and separating them using commas. This rudimentary approach would not be expected by a consumer of a professional control. In this version of the ImageRotator, we want to enhance the data entry of the images by providing an editor that can be used to add image URL entries and have those entries placed into the body of our ImageRotator HTML tag. If we go back to the ScriptManager control, this is the experience it provides when adding web service or script references while in the Properties window.
The ImageList property in this version of the ImageRotator uses two editors to provide a rich design-time experience when adding ImageUrl entries. The first editor is a Collection editor, shown in Figure 11.5, and is designed to assist in adding, editing, and removing values that are based on a Collection. The editor is automatically associated with our ImageList property because the type of the property is a Collection. The second editor we will use is the ImageUrlEditor, shown in Figure 11.6, which the ImageUrl entry uses to assist the user in entering a URL. This editor is associated with the Url property of the ImageUrl class, as shown in Listing 11.9, by adding the Editor attribute to the property. We use the Editor attribute to configure which editor to use when adding values to the property in the designer. In our case, we are using the ImageUrlEditor to provide the user with a clean way to find an image located in a web application and assign the value to the ImageUrl property. The use of the associated UrlProperty attribute provides a filter that identifies specific file types that can be used to filter against the ImageUrl property.
Figure 11.5 Image URL Collection Editor
Figure 11.6 Image URL Editor
Listing 11.9. ImageUrl Class
[Serializable] public class ImageUrl { [DefaultValue(""),Bindable(true), Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)), UrlProperty] public string Url { get; set; } }
Create a Type Converter to Support Serialization
The use of complex types presents a few challenges in the ASP.NET AJAX Control Toolkit during the script generation process. The problem arises in how the ASP.NET AJAX Control Toolkit serializes your extender control properties as it creates the $create statement. By default, the toolkit tries to get the default string representation of the value your property represents. In most cases, this is an easy task because most simple types convert to a string relatively easily. If you are using complex types, however, this can present a problem because the default ConvertToString() representation of a complex object is its type name. To resolve this issue, you must create a type converter and associate it with the complex type. When the ASP.NET AJAX Control Toolkit encounters a type during script generation, it looks to see whether the type has a converter. If it does, it uses the converter to convert the data instead of using the default ConvertToString() method. In this section, we walk through creating a System.ComponentModel.TypeConverter that will be used to convert our ImageUrlList type into a JavaScript Object Notation (JSON) string that can be consumed on the client.
The ImageListConverter, shown in Listing 11.10, is designed to convert the ImageList to a JSON array of image URLs that are then passed back to the client. The creation of this type converter now enables us to return a data format that the client can use instead of a string that contains the type name of the ImageList. For the type converter to be used, we need to associate it with the ImageList type. We do this by adding the TypeConverter attribute to the ImageList class, as shown in Listing 11.11, and assigning the type of the ImageList to it. Now when the toolkit performs a ConvertToString on the ImageList, the JSON string representation of the ImageList will be returned.
Listing 11.10. ImageListConverter Type Converter Class
public class ImageListConverter : TypeConverter { public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { Collection<ImageUrl> imageList = value as Collection<ImageUrl>; if (imageList != null && destinationType == typeof(string)) { StringBuilder builder = new StringBuilder(); builder.Append("["); bool first = true; foreach (ImageUrl imageUrl in imageList) { if(first) { first = false; } else { builder.Append(","); } builder.Append("\""); builder.Append(imageUrl.Url.Replace("~/", "")); builder.Append("\""); } builder.Append("]"); return builder.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } }
Listing 11.11. ImageUrlList Collection Class
[Serializable] [TypeConverter(typeof(ImageListConverter))] public class ImageUrlList : Collection<ImageUrl> { }