ptg 1634 CHAPTER 36 Building Custom Controls get { return _title; } set { _title = value; } } [Category(“Movie”)] [Description(“Movie Description”)] public string Description { get { return _description; } set { _description = value; } } protected override void RenderContents(HtmlTextWriter writer) { writer.RenderBeginTag(HtmlTextWriterTag.H1); writer.Write(_title); writer.RenderEndTag(); writer.Write(_description); } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Div; } } } } The page in Listing 36.33 contains the MovieView control. Open the page in Design view to see the effect of the various design-time attributes. For example, a category and descrip- tion are associated with both the Title and Description properties in the Properties window (see Figure 36.15). From the Library of Wow! eBook ptg 1635 Creating a Better Designer Experience 36 LISTING 36.33 ShowMovieView.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”custom” Namespace=”myControls” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ➥ “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show MovieView</title> </head> <body> <form id=”form1” runat=”server”> <div> <custom:MovieView id=”MovieView1” Runat=”server” /> </div> </form> </body> </html> FIGURE 36.15 The MovieView control in Design view. From the Library of Wow! eBook ptg 1636 CHAPTER 36 Building Custom Controls Creating Control Designers You can modify the appearance of your custom controls in Design view by creating a ControlDesigner. The ASP.NET Framework enables you to implement a number of fancy features when you implement a ControlDesigner. This section focuses on just two of these advanced features. First, you learn how to create a ContainerControlDesigner. A ContainerControlDesigner enables you to drag and drop other controls from the Toolbox onto your control in Design view. You also learn how to add Smart Tags (also called Action Lists) to your control. When a control supports Smart Tags, a menu of common tasks pop up above the control in Design view. Creating a Container ControlDesigner If you associate a custom control with a ContainerControlDesigner, you can add child controls to your control in Design view. For example, the file in Listing 36.34 contains a GradientPanel control. This control displays a gradient background behind its contents (see Figure 36.16). FIGURE 36.16 Displaying the GradientPanel control. From the Library of Wow! eBook ptg 1637 Creating a Better Designer Experience 36 LISTING 36.34 GradientPanel.cs using System; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.Design; using System.ComponentModel; using System.Drawing; namespace myControls { [Designer(typeof(GradientPanelDesigner))] [ParseChildren(false)] public class GradientPanel : WebControl { private GradientDirection _direction = GradientDirection.Horizontal; private Color _startColor = Color.DarkBlue; private Color _endColor = Color.White; public GradientDirection Direction { get { return _direction; } set { _direction = value; } } public Color StartColor { get { return _startColor; } set { _startColor = value; } } public Color EndColor { get { return _endColor; } set { _endColor = value; } } protected override void AddAttributesToRender(HtmlTextWriter writer) { writer.AddStyleAttribute(HtmlTextWriterStyle.Filter, this.GetFilter ➥ String()); base.AddAttributesToRender(writer); } public string GetFilterString() { From the Library of Wow! eBook ptg 1638 CHAPTER 36 Building Custom Controls return String.Format( “progid:DXImageTransform.Microsoft.Gradient(gradientType={0},startColorStr={1}, ➥ endColorStr={2})”, _ direction.ToString(“d”), ColorTranslator.ToHtml(_startColor), ColorTranslator.ToHtml(_endColor)); } public GradientPanel() { this.Width = Unit.Parse(“500px”); } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Div; } } } public enum GradientDirection { Vertical = 0, Horizontal = 1 } public class GradientPanelDesigner : ContainerControlDesigner { protected override void AddDesignTimeCssAttributes( System.Collections.IDictionary styleAttributes) { GradientPanel gPanel = (GradientPanel)this.Component; styleAttributes.Add(“filter”, gPanel.GetFilterString()); base.AddDesignTimeCssAttributes(styleAttributes); } } } The GradientPanel control uses an Internet Explorer filter to create the gradient back- ground. The filter is applied in the AddAttributesToRender() method. You can set the StartColor, EndColor, and Direction properties to control the appearance of the gradient background. From the Library of Wow! eBook ptg 1639 Creating a Better Designer Experience 36 The GradientPanel control is decorated with a ControlDesigner attribute. This attribute associates the GradientPanelDesigner class with the GradientPanel control. The GradientPanelDesigner is also included in Listing 36.34. One method is overridden in the GradientPanelDesigner class. The AddDesignTimeCssAttributes() method is used to apply the gradient background in Design view. WARNING The file in Listing 36.34 doesn’t compile unless you add a reference to the System.Design.dll assembly to your application. You can add the necessary refer- ence by selecting Website, Add Reference and selecting the System.Design assembly. The page in Listing 36.35 illustrates how you can declare the GradientPanel in a page. However, to understand the effect of the ContainerControlDesigner, you need to open the page in Design view in either Visual Web Developer or Visual Studio .NET. LISTING 36.35 ShowGradientPanel.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”custom” Namespace=”myControls” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ➥ “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show GradientPanel</title> </head> <body> <form id=”form1” runat=”server”> <div> <custom:GradientPanel id=”GradientPanel1” Runat=”server”> <asp:Calendar ID=”Calendar1” runat=”server” /> </custom:GradientPanel> </div> </form> </body> </html> From the Library of Wow! eBook ptg 1640 CHAPTER 36 Building Custom Controls When you open the page in Listing 36.35 in Design view, you can drag other controls from the toolbox onto the GradientPanel control. For example, if you drag a Calendar control onto the GradientPanel control, the Calendar control is added automatically to the control collection of the GradientPanel (see Figure 36.17). Adding Smart Tasks If you add a GridView control to a page when you are in Design view, a menu of common tasks appears above the GridView. For example, you can select a Smart Task to enable sorting or paging. You can add your own Smart Tasks to a custom control by inheriting a new class from the base DesignerActionList class. For example, the file in Listing 36.36 contains three classes. It contains a custom control, named the SmartImage control, which enables you to rotate and mirror images. It also contains a ControlDesigner. Finally, it contains a DesignerActionList class that contains two Smart Tasks. LISTING 36.36 SmartImage.cs using System; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.Design; using System.ComponentModel; using System.ComponentModel.Design; FIGURE 36.17 Editing the GradientPanel control in Design view. From the Library of Wow! eBook ptg 1641 Creating a Better Designer Experience 36 namespace myControls { [Designer(typeof(SmartImageDesigner))] public class SmartImage : WebControl { string _imageUrl; string _alternateText; int _rotation = 0; bool _mirror = false; public string ImageUrl { get { return _imageUrl; } set { _imageUrl = value; } } public string AlternateText { get { return _alternateText; } set { _alternateText = value; } } public int Rotation { get { return _rotation; } set { _rotation = value; } } public bool Mirror { get { return _mirror; } set { _mirror = value; } } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Img; } } private string GetFilterString() { string _mirrorValue = “0”; From the Library of Wow! eBook ptg 1642 CHAPTER 36 Building Custom Controls if (_mirror) _mirrorValue = “1”; return String.Format( “progid:DXImageTransform.Microsoft.BasicImage(Rotation={0},Mirror={1})”, _rotation, _mirrorValue); } protected override void AddAttributesToRender(HtmlTextWriter writer) { writer.AddStyleAttribute(HtmlTextWriterStyle.Filter, this.GetFilter ➥ String()); writer.AddAttribute(HtmlTextWriterAttribute.Src, _imageUrl); writer.AddAttribute(HtmlTextWriterAttribute.Alt, _alternateText); base.AddAttributesToRender(writer); } } public class SmartImageDesigner : ControlDesigner { public override DesignerActionListCollection ActionLists { get { DesignerActionListCollection actionLists = new DesignerActionListCollection(); actionLists.AddRange(base.ActionLists); actionLists.Add(new SmartImageActionList(this)); return actionLists; } } } public class SmartImageActionList : DesignerActionList { private DesignerActionItemCollection items; private SmartImageDesigner _parent; public SmartImageActionList(SmartImageDesigner parent) : base(parent.Component) { From the Library of Wow! eBook ptg 1643 Creating a Better Designer Experience 36 _parent = parent; } public void Rotate() { TransactedChangeCallback toCall = new TransactedChangeCallback(DoRotate); ControlDesigner.InvokeTransactedChange(this.Component, toCall, “Rotate”, “Rotate image 90 degrees”); } public void Mirror() { TransactedChangeCallback toCall = new TransactedChangeCallback(DoMirror); ControlDesigner.InvokeTransactedChange(this.Component, toCall, “Mirror”, “Mirror Image”); } public override DesignerActionItemCollection GetSortedActionItems() { if (items == null) { items = new DesignerActionItemCollection(); items.Add(new DesignerActionMethodItem(this, “Rotate”, “Rotate Image”, true)); items.Add(new DesignerActionMethodItem(this, “Mirror”, “Mirror Image”, true)); } return items; } public bool DoRotate(object arg) { SmartImage img = (SmartImage)this.Component; img.Rotation += 1; if (img.Rotation > 3) img.Rotation = 0; _parent.UpdateDesignTimeHtml(); return true; } public bool DoMirror(object arg) { SmartImage img = (SmartImage)this.Component; img.Mirror = !img.Mirror; _parent.UpdateDesignTimeHtml(); From the Library of Wow! eBook . the effect of the various design-time attributes. For example, a category and descrip- tion are associated with both the Title and Description properties in the Properties window (see Figure 36.15). . need to open the page in Design view in either Visual Web Developer or Visual Studio .NET. LISTING 36.35 ShowGradientPanel.aspx <%@ Page Language=”C#” %> <%@ Register TagPrefix=”custom”. System.ComponentModel; using System.Drawing; namespace myControls { [Designer(typeof(GradientPanelDesigner))] [ParseChildren(false)] public class GradientPanel : WebControl { private GradientDirection