Up to this point, we've ignored the Design view experience. In other words, we've ignored the question of how our custom controls appear in the Visual Web Developer or Visual Studio .NET Design view. You can modify the appearance of your control in Design view in two ways. You can apply design-time attributes to the control, or you can associate a ControlDesigner with your control. We'll explore both methods in this section. Applying Design-Time Attributes to a Control Design-time attributes enable you to modify how control properties appear in Design view. Some attributes are applied to the control itself, whereas other attributes are applied to particular properties of a control. Here is the list of the design-time attributes you can apply to a control: DefaultEvent Enables you to specify the default event for a control. When you double-click a control in Visual Web Developer or Visual Studio .NET, an event handler is automatically created for the default event. DefaultProperty Enables you to specify the default property for a control. When you open the Property window for a control, this property is highlighted by default. PersistChildren Enables you to specify whether child controls or properties are persisted as control attributes or control contents. ToolboxData Enables you to specify the tag added to a page when a control is dragged from the toolbox. ToolboxItem Enables you to block a control from appearing in the Toolbox. Here is the list of design-time attributes you can apply to a control property: Bindable Enables you to indicate to display a Databindings dialog box for the property. Browsable Enables you to block a property from appearing in the Properties window. Category Enables you to specify the category associated with the property. The property appears under this category in the Properties window. DefaultValue Enables you to specify a default value for the property. When you right-click a property in the Properties window, you can select Reset to the return the property to its default value. Description Enables you to specify the description associated with the property. The description appears in the Properties window when the property is selected. DesignerSerializationVisibility Enables you to specify how changes to a property are serialized. Possible values are Visible, Hidden, and Content. Editor Enables you to specify a custom editor for editing the property in Design view. EditorBrowsable Enables you to block a property from appearing in Intellisense. NotifyParentProperty Enables you to specify that changes to a subproperty should be propagated to the parent property. PersistenceMode Enables you to specify whether a property is persisted as a control attribute or control content. Possible values are Attribute, EncodedInnerDefaultProperty, InnerDefaultProperty, and InnerProperty. TypeConverter Enables you to associate a custom type converter with a property. A type converter converts a property between a string representation and a type (or vice versa). The Editor attribute enables you to associate a particular editor with a property. Certain types in the Framework have default editors. For example, a property which represents a System.Drawing.Color value is automatically associated with the ColorEditor. The ColorEditor displays a color picker (see Figure 31.14). To view the list of editors included in the .NET Framework, look up the UITypeEditor class in the .NET Framework SDK Documentation. Figure 31.14. Using the ColorEditor to pick a color. The MovieView control contained in Listing 31.32 illustrates how you can use several of these attributes. The control displays a single movie. Listing 31.32. MovieView.vb Imports System Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.ComponentModel Namespace myControls <DefaultProperty("Title")> _ Public Class MovieView Inherits WebControl Private _title As String = "Movie Title" Private _description As String = "Movie Description" <Category("Movie")> _ <Description("Movie Title")> _ Public Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = Value End Set End Property <Category("Movie")> _ <Description("Movie Description")> _ Public Property Description() As String Get Return _description End Get Set(ByVal Value As String) _description = Value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.RenderBeginTag(HtmlTextWriterTag.H1) writer.Write(_title) writer.RenderEndTag() writer.Write(_description) End Sub Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag Get Return HtmlTextWriterTag.Div End Get End Property End Class End Namespace | The page in Listing 31.33 contains the MovieView control. Open the page in Design view to see the effect of the various design-time attributes. For example, notice that a category and description are associated with both the Title and Description properties in the Properties window (see Figure 31.15). Figure 31.15. The MovieView control in Design view. Listing 31.33. ShowMovieView.aspx [View full width] <%@ Page Language="VB" %> <%@ 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 runat="server"> <title>Show MovieView</title> </head> <body> <form runat="server"> <div> <custom:MovieView Runat="server" /> </div> </form> </body> </html> | Creating Control Designers You can modify the appearance of your custom controls in Design view by creating a ControlDesigner. The ASP.NET 2.0 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, then you can add child controls to your control in Design view. For example, the file in Listing 31.34 contains a GradientPanel control. This control displays a gradient background behind its contents (see Figure 31.16). Figure 31.16. Displaying the GradientPanel control. Listing 31.34. GradientPanel.vb The GradientPanel control uses an Internet Explorer filter to create the gradient background. 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. Notice that 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 31.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 31.34 doesn't compile unless you add a reference to the System.Design.dll assembly to your application. You can add the necessary reference by selecting the menu option Website, Add Reference and selecting the System.Design assembly. The page in Listing 31.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 31.35. ShowGradientPanel.aspx [View full width] <%@ Page Language="VB" %> <%@ 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 runat="server"> <title>Show GradientPanel</title> </head> <body> <form runat="server"> <div> <custom:GradientPanel Runat="server"> <asp:Calendar runat="server" /> </custom:GradientPanel> </div> </form> </body> </html> | When you open the page in Listing 31.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 31.17). Figure 31.17. Editing the GradientPanel control in Design view. Adding Smart Tasks If you add a GridView control to a page when you are in Design view, you'll notice that 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 31.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 31.36. SmartImage.vb [View full width] Imports System Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Web.UI.Design Imports System.ComponentModel Imports System.ComponentModel.Design Namespace myControls <Designer(GetType(SmartImageDesigner))> _ Public Class SmartImage Inherits WebControl Private _imageUrl As String Private _alternateText As String Private _rotation As Integer = 0 Private _mirror As Boolean = False Public Property ImageUrl() As String Get Return _imageUrl End Get Set(ByVal Value As String) _imageUrl = Value End Set End Property Public Property AlternateText() As String Get Return _alternateText End Get Set(ByVal Value As String) _alternateText = Value End Set End Property Public Property Rotation() As Integer Get Return _rotation End Get Set(ByVal Value As Integer) _rotation = Value End Set End Property Public Property Mirror() As Boolean Get Return _mirror End Get Set(ByVal Value As Boolean) _mirror = Value End Set End Property Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag Get Return HtmlTextWriterTag.Img End Get End Property Private Function GetFilterString() As String Dim _mirrorValue As String = "0" If _mirror Then _mirrorValue = "1" End If Return String.Format("progid:DXImageTransform.Microsoft.BasicImage(Rotation={0 },Mirror={1})", _rotation, _mirrorValue) End Function Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter) writer.AddStyleAttribute(HtmlTextWriterStyle.Filter, Me.GetFilterString()) writer.AddAttribute(HtmlTextWriterAttribute.Src, _imageUrl) writer.AddAttribute(HtmlTextWriterAttribute.Alt, _alternateText) MyBase.AddAttributesToRender(writer) End Sub End Class Public Class SmartImageDesigner Inherits ControlDesigner Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection Get Dim colActions As New DesignerActionListCollection() colActions.AddRange(MyBase.ActionLists) colActions.Add(New SmartImageActionList(Me)) Return colActions End Get End Property End Class Public Class SmartImageActionList Inherits DesignerActionList Private items As DesignerActionItemCollection Private _parent As SmartImageDesigner Public Sub New(ByVal parent As SmartImageDesigner) MyBase.New(parent.Component) _parent = parent End Sub Public Sub Rotate() Dim toCall As TransactedChangeCallback = New TransactedChangeCallback (AddressOf DoRotate) ControlDesigner.InvokeTransactedChange(Me.Component, toCall, "Rotate", "Rotate image 90 degrees") End Sub Public Sub Mirror() Dim toCall As TransactedChangeCallback = New TransactedChangeCallback (AddressOf DoMirror) ControlDesigner.InvokeTransactedChange(Me.Component, toCall, "Mirror", "Mirror Image") End Sub Public Overrides Function GetSortedActionItems() As DesignerActionItemCollection If IsNothing(items) Then items = New DesignerActionItemCollection() items.Add(New DesignerActionMethodItem(Me, "Rotate", "Rotate Image", True)) items.Add(New DesignerActionMethodItem(Me, "Mirror", "Mirror Image", True)) End If Return items End Function Public Function DoRotate(ByVal arg As Object) As Boolean Dim img As SmartImage = CType(Me.Component, SmartImage) img.Rotation += 1 If img.Rotation > 3 Then img.Rotation = 0 End If _parent.UpdateDesignTimeHtml() Return True End Function Public Function DoMirror(ByVal arg As Object) As Boolean Dim img As SmartImage = CType(Me.Component, SmartImage) img.Mirror = Not img.Mirror _parent.UpdateDesignTimeHtml() Return True End Function End Class End Namespace | The SmartImage control takes advantage of an Internet Explorer filter named the BasicImage filter. This filter enables you to manipulate images by rotating, mirroring, and changing the opacity of images. In Listing 31.36, the filter is applied in the AddAttributesToRender() method. The SmartImage control is associated with a ControlDesigner named the SmartImageDesigner through the control's Designer attribute. The SmartImageDesigner class overrides the base class's ActionLists property to expose a custom DesignerActionList. The DesignerActionList is the final class declared in Listing 31.36. This class contains four methods named Rotate(), DoRotate(), Mirror(), and DoMirror(). The GetSortedActionItems() method exposes the Rotate and Mirror actions. When all is said and done, the custom ActionList enables you to display Rotate and Mirror Smart Tags for the SmartImage control in Design view. When you click the Rotate action in Design view, the SmartImage does in fact rotate (see Figure 31.18). Figure 31.18. Adding Smart Tags to a control. Note You can view the SmartImage control by opening the ShowSmartImage.aspx page included on the CD that accompanies this book. |