Designers


A designer is a class that governs the behavior of a component on the design surface. In the .NET Framework, the System.ComponentModel.Design.IDesigner interface specifies core designer functionality. The System.ComponentModel.Design.ComponentDesigner class implements this interface and provides base designer functionality to all ( designable ) components . The core designer architecture is the same in Web Forms and Windows Forms. However, Web Forms and Windows Forms use different rendering engines. The Web Forms designer uses Microsoft Internet Explorer as its rendering engine, while the Windows Forms designer uses GDI+ for rendering. To provide base design-time rendering logic that is relevant to each rendering engine, Web Forms and Windows Forms define their own base designer classes that derive from ComponentDesigner . The base class for Web Forms control designers, System.Web.UI.Design.ControlDesigner , implements the plumbing to generate HTML for rendering a control at design time. The base class for Windows Forms control designers, System.Windows.Forms.Design.ControlDesigner , provides the plumbing for drawing in a native window by using GDI+ at design time.

The System.Web.UI.Design.ControlDesigner class is associated with the base Control class (via the DesignerAttribute ) and thus is associated with a server control by default. From here on, we'll simply refer to the base designer class for server controls as ControlDesigner because there is no potential for ambiguity. (We do not describe designers for Windows Forms controls in this book.)

To develop a custom control designer, you must implement a class that derives directly or indirectly from ControlDesigner and overrides the methods described in the following list. You can then associate the designer class with your control by applying the DesignerAttribute metadata attribute, which we'll describe in greater detail at the end of this section.

  • GetDesignTimeHtml

    Returns the string that contains the HTML to render at design time. ControlDesigner implements this method to return the HTML generated by the control at run time by calling the control's RenderControl method. We will describe the implementation pattern for this method shortly.

  • GetEmptyDesignTimeHtml

    Returns the HTML to render at design time when the HTML generated by calling RenderControl returns an empty string. This can happen when the set of properties required for rendering the control have not been set by the page developer. ControlDesigner 's implementation of this method returns a string that indicates the type of the control and its ID, thus enabling identification of the control on the design surface. You can override this method to generate placeholder design-time HTML along with an instructive message for the page developer by using the CreatePlaceHolderDesignTimeHtml helper method.

  • GetErrorDesignTimeHtml

    Returns HTML to render at design time when an error is encountered while generating the design-time HTML. ControlDesigner returns an empty string from this method. You can override this method to generate a useful error message in the design-time HTML by invoking the CreatePlaceHolderDesignTimeHtml helper method.

  • Initialize

    Invoked when the designer is initialized and associated with a component. The base class's implementation of this method checks that the component to which the designer is being attached derives from Control . You should override this method to check that the designer is being associated with the control type that you intended it to be used with.

The most important method to implement in a designer is the GetDesignTimeHtml method. The pseudocode in Listing 15-1 shows the implementation pattern for this method.

Listing 15-1 Pattern for implementing the GetDesignTimeHtml method
 publicoverridestringGetDesignTimeHtml(){ stringdesignTimeHtml=null; try{ //Makechangestothecontrolifneededtoensurea //meaningfuldesign-timerendering.  //Nextinvokethebaseclass'smethod. designTimeHtml=base.GetDesignTimeHtml() } catch(Exceptionex){ designTimeHtml=GetErrorDesignTimeHtml(ex); } finally{ //Undoanychangesthatyoumadeinthetryblock. } if((designTimeHtml==null)(designTimeHtml.Length==0)){ designTimeHtml=GetEmptyDesignTimeHtml(); } returndesignTimeHtml; } 

Note

If you make changes to a control in GetDesignTimeHtml (for example, a Label renders the value of its ID property as its text at design time when its Text property has not been set), you must undo those changes after you have generated the HTML string to render. You should enclose your code in try / catch / finally blocks so that you can undo changes (if any) made in the try block in the finally block.


The base ControlDesigner class sets a control's Visible property to true in the try block of its GetDesignTimeHtml method so that every control on the page is rendered at design time, even if the control's Visible property is set to false . ControlDesigner sets the value of the Visible property back to its actual value in the finally block of the GetDesignTimeHtml method.

ASP.NET provides two additional base designer classes that derive from ControlDesigner and provide designer functionality for common scenarios:

  • System.Web.UI.Design.TemplatedControlDesigner

    Implements the base functionality to render a design-time UI for editing template properties of templated controls such as DataList in a WYSIWIG fashion.

  • System.Web.UI.Design.ReadWriteControlDesigner

    Implements the base functionality to enable a page developer to select and modify the contents of a container control such as a Panel in a WYSIWIG fashion.

We'll now look at examples that illustrate how you can extend the base designer classes to implement designers for common categories of server ­controls.

Composite Control Designers ”The CompositeControlDesigner Example

As we described in Chapter 12, "Composite Controls," ASP.NET does not provide a base designer class for composite controls. When you implement a composite control, such as the CompositeLogin control we described in Chapter 12, you must implement a designer that ensures that child controls are rendered on the design surface. Listing 15-2 shows the code for a designer that you can use as the base class for composite controls.

Listing 15-2 CompositeControlDesigner.cs
 usingSystem; usingSystem.ComponentModel; usingSystem.ComponentModel.Design; usingSystem.Web.UI; usingSystem.Web.UI.Design; namespaceMSPress.ServerControls.Design{ publicclassCompositeControlDesigner:ControlDesigner{ publicoverridestringGetDesignTimeHtml(){ //Retrievethecontrolstoensuretheyarecreated. ControlCollectioncontrols=((Control)Component).Controls; returnbase.GetDesignTimeHtml(); } publicoverridevoidInitialize(IComponentcomponent){ if(!(componentisControl)&& !(componentisINamingContainer)){ thrownewArgumentException("Componentmustbeacontainercontrol.",  "component"); } base.Initialize(component); } } } 

Although this designer ensures that child controls are rendered, it is not a read-write control designer ”that is, a page developer cannot select a child control on the design surface to access its object model in the property browser. Later in this section, we'll describe the ReadWriteControlDesigner class, which allows page developers to select a control's contents on the design surface. However, a ReadWriteControlDesigner renders only those child controls that exist within the control's tags in the declarative syntax of the .aspx page. In short, the design-time architecture currently does not provide any functionality to allow a page developer to access child controls that are programmatically added to your control.

To associate the CompositeControlDesigner designer with a composite control, apply the DesignerAttribute metadata attribute, as shown in the following example:

 [ Designer(typeof(CompositeControlDesigner)) ] publicclassCompositeLogin:WebControl{...} 

We'll describe other variations of the DesignerAttribute at the end of this section.

Templated Control Designers ”The ContactInfoDesigner Example

We'll now examine how to implement a designer that provides a UI for editing template properties in a WYSIWYG fashion. To illustrate the concepts, we'll create a ContactInfoDesigner designer for the ContactInfo templated control we implemented in Chapter 12.

Figure 15-2 shows the ContactInfo control with which the ContactInfo ­Designer class is associated. The Contact Template command appears when the page developer right-clicks the control to access the context menu and selects the Edit Template command.

Figure 15-2. The ContactInfo control with which ContactInfoDesigner is associated in the Web Forms designer in Visual Studio .NET

graphics/f15hn02.jpg

Figure 15-3 shows the control after the page developer chooses the Contact Template command shown in Figure 15-2. The control now displays the template editing UI, and the page developer can create the template in a WYSIWYG fashion by dragging controls from the toolbox onto the template editing surface. To set properties of controls in the template, the page developer selects controls on the template surface and edits their properties in the property browser. To complete editing the template, the page developer right-clicks the control to access the context menu and then chooses the End Template Editing command.

Figure 15-3. The template editing UI created by the ContactInfoDesigner class that is associated with the ContactInfo control

graphics/f15hn03.jpg

Before we examine the code for the ContactInfoDesigner class, here is a high-level overview of the steps required to implement a designer for a templated control. You must define a class that derives from System.Web.UI.Design.TemplatedControlDesigner and implement the following abstract methods of the TemplatedControlDesigner base class:

  • GetCachedTemplateEditingVerbs

    Implement this method to return one or more template editing verbs . A template editing verb represents a command or action that enables template editing. In Visual Studio .NET a template editing verb creates a command in the context menu that launches the template editing UI. The Contact Template command in Figure 15-2 represents a template editing verb. A template editing verb is a special case of a designer verb , which we'll describe in more detail toward the end of this section. Designer verbs represent commands on the design surface.

  • CreateTemplateEditingFrame

    Implement this method to define the template editing UI. Figure 15-3 shows the template editing UI created by the ContactInfoDesigner .

  • GetTemplateContent

    Implement this method to return the text corresponding to the current template definition. The text is the declarative syntax for the template in the .aspx page.

  • SetTemplateContent

    Implement this method to generate a new template instance from the modified contents of the template on the design surface.

A templated control designer also uses an ITemplateEditingService service, which is one of the services represented by the Design-Time Services box shown in Figure 15-1.

Listing 15-3 shows the code for the ContactInfoDesigner class.

Listing 15-3 ContactInfoDesigner.cs
 usingSystem; usingSystem.Collections; usingSystem.ComponentModel; usingSystem.ComponentModel.Design; usingSystem.Diagnostics; usingSystem.Web.UI; usingSystem.Web.UI.Design; usingSystem.Web.UI.WebControls; usingMSPress.ServerControls; namespaceMSPress.ServerControls.Design{ publicclassContactInfoDesigner:TemplatedControlDesigner{ privateTemplateEditingVerb[]_templateEditingVerbs; #regionDesign-timeHTML publicoverridestringGetDesignTimeHtml(){ ContactInfocontrol=(ContactInfo)Component; if(control.ContactTemplate==null){ returnGetEmptyDesignTimeHtml(); } 
 stringdesignTimeHtml=String.Empty; try{ control.DataBind(); designTimeHtml=base.GetDesignTimeHtml(); } catch(Exceptione){ designTimeHtml=GetErrorDesignTimeHtml(e); } returndesignTimeHtml; } protectedoverridestringGetEmptyDesignTimeHtml(){ returnCreatePlaceHolderDesignTimeHtml("Right-clickto " +  "edittheContactTemplateproperty. " +  "<br>IftheContactTemplateisnotspecified,a " +  "defaulttemplateisusedatruntime."); } protectedoverridestringGetErrorDesignTimeHtml(Exceptione){ returnCreatePlaceHolderDesignTimeHtml("Therewasan " +  "errorrenderingtheContactInfocontrol."); } #endregionDesign-timeHTML #regionTemplate-editingFunctionality protectedoverride TemplateEditingVerb[]GetCachedTemplateEditingVerbs(){ if(_templateEditingVerbs==null){ _templateEditingVerbs=newTemplateEditingVerb[1]; _templateEditingVerbs[0]= newTemplateEditingVerb("ContactTemplate", 0,this); } return_templateEditingVerbs; } protectedoverride ITemplateEditingFrameCreateTemplateEditingFrame(TemplateEditingVerbverb){ ITemplateEditingFrameframe=null; if((_templateEditingVerbs!=null)&& (_templateEditingVerbs[0]==verb)){ ITemplateEditingServiceteService= (ITemplateEditingService)GetService(typeof(ITemplateEditingService)); if(teService!=null){ Stylestyle= ((ContactInfo)Component).ControlStyle; frame=teService.CreateFrame(this,verb.Text, newstring[]{ "ContactTemplate" },style, null); } } returnframe; } privatevoidDisposeTemplateEditingVerbs(){ if(_templateEditingVerbs!=null){ _templateEditingVerbs[0].Dispose(); _templateEditingVerbs=null; } } publicoverridestringGetTemplateContent(ITemplateEditingFrameeditingFrame,stringtemplateName, outboolallowEditing){ stringcontent=String.Empty; allowEditing=true; if((_templateEditingVerbs!=null)&& (_templateEditingVerbs[0]==editingFrame.Verb)){ ITemplatecurrentTemplate= ((ContactInfo)Component).ContactTemplate; if(currentTemplate!=null){ content=GetTextFromTemplate(currentTemplate); } } returncontent; } publicoverridevoidSetTemplateContent(ITemplateEditingFrameeditingFrame,stringtemplateName, stringtemplateContent){ if((_templateEditingVerbs!=null)&& (_templateEditingVerbs[0]==editingFrame.Verb)){ ContactInfocontrol=(ContactInfo)Component; ITemplatenewTemplate=null; if((templateContent!=null)&& (templateContent.Length!=0)){ newTemplate=GetTemplateFromText(templateContent); } control.ContactTemplate=newTemplate; } } #endregionTemplate-editingFunctionality publicoverrideboolAllowResize{ get{ booltemplateExists= ((ContactInfo)Component).ContactTemplate!=null; //Whentemplatesarenotdefined,renderaread-only //fixed-sizeblock.Oncetemplatesaredefinedor //arebeingedited,thecontrolshouldallowresizing. returntemplateExistsInTemplateMode; } } protectedoverridevoidDispose(booldisposing){ if(disposing){ DisposeTemplateEditingVerbs(); } base.Dispose(disposing); } publicoverridevoidInitialize(IComponentcomponent){ if(!(componentisContactInfo)){ thrownewArgumentException("ComponentmustbeaContactInfocontrol.",  "component"); } base.Initialize(component); } publicoverridevoidOnComponentChanged(objectsender, ComponentChangedEventArgsce){ base.OnComponentChanged(sender,ce); if(ce.Member!=null){ stringname=ce.Member.Name; if(name.Equals("Font") name.Equals("ForeColor") name.Equals("BackColor")){ DisposeTemplateEditingVerbs(); } } } } } 

ContactInfoDesigner demonstrates how to implement the abstract methods of the base TemplatedControlDesigner class by performing the following tasks :

  • Implementing the GetCachedTemplateEditingVerbs method to create a TemplateEditingVerb that appears as a command in the context menu and enables a page developer to launch the UI for editing a template property. A designer for a templated control that has more than one template property can create additional verbs that correspond to other templates or can provide verbs that correspond to template groups. For example, the DataList control creates three template editing verbs: Header And Footer Templates, Item Templates, and Separator Template.

  • Implementing the CreateTemplateEditingFrame method to create and return the template editing UI corresponding to a given template editing verb. In this method, ContactInfoDesigner invokes the GetService method of the base class to get an ITemplateEditingService instance. ContactInfoDesigner then invokes the CreateFrame method of the ITemplateEditingService interface to create an ITemplateEditingFrame instance. A template editing frame can contain more than one template. For example, the Item Templates frame created by the designer of the DataList control allows a page developer to modify four templates: ItemTemplate , AlternatingItemTemplate , SelectedItemTemplate , and EditItemTemplate .

  • Implementing the GetTemplateContent method to return the text for the current template. To perform this task, ContactInfo invokes the GetTextFromTemplate helper method of the base class and passes the value of the current template property into it.

  • Implementing the SetTemplateContent method to generate the template instance from the modified template definition. To perform this task, ContactInfo invokes the GetTemplateFromText helper method of the base class. ContactInfo then assigns the value of the template instance to the ContactInfo template property of the control. This assignment subsequently causes the control to render content corresponding to the new template when the GetDesignTimeHtml method of the base designer invokes the control's RenderControl method.

In addition, ContactInfoDesigner overrides the following members it inherits from ControlDesigner , which is the base class for TemplatedControlDesigner :

  • Overrides the GetDesignTimeHtml method to invoke DataBind on the control in the method's try block. As with the page, a designer must invoke DataBind on a templated control to ensure that data binding expressions in the template or templates are evaluated.

  • Overrides the GetEmptyDesignTimeHtml and GetErrorDesignTimeHtml methods to generate appropriate messages when there is no HTML to render or when there is an error in rendering the control on the design surface.

  • Overrides the AllowResize property to enable a page developer to resize the control at design time only when the template has content or when the control is in template editing mode.

  • Overrides the Dispose method to free the cached template editing verbs in a more deterministic manner than waiting for garbage ­collection.

  • Overrides the OnComponentChanged method to dispose the cached template editing verbs if the page developer changes the Font , ForeColor , or BackColor style properties of the control. This action causes the designer to propagate the changes in these properties to the template editing UI when the template editing verbs are re-created.



Developing Microsoft ASP. NET Server Controls and Components
Developing Microsoft ASP.NET Server Controls and Components (Pro-Developer)
ISBN: 0735615829
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net