Implementing a Custom Typed Style ” The MyPanelStyle ExampleWe'll now demonstrate how to implement and use a custom typed style by creating a MyPanel control and its associated typed style, MyPanelStyle . The MyPanel control is similar to the ASP.NET Panel control, which allows a page developer to add controls to its Controls collection by nesting them within the control's tags. In a visual designer, you can do this by dragging controls onto the Panel 's design surface. The ASP.NET Panel control does not have an associated PanelStyle and does not follow the recommended pattern of encapsulating style- related behavior in a typed style. Instead, Panel defines its style properties just as it defines other nonstyle properties. We'll demonstrate the technique that you should follow to add style properties by implementing a MyPanelStyle typed style and delegating the style functionality of the MyPanel control to it. In terms of functionality, MyPanel is identical to the Panel control and exposes the three style properties in the following list. Note that these properties are actually properties of the MyPanelStyle typed style.
We'll first show how the MyPanel control uses the MyPanelStyle typed style, and then we'll describe MyPanelStyle . Listing 11-3 shows the code for the MyPanel control. Listing 11-3 MyPanel.csusingSystem; usingSystem.ComponentModel; usingSystem.ComponentModel.Design; usingSystem.Drawing.Design; usingSystem.Web.UI; usingSystem.Web.UI.Design; usingSystem.Web.UI.WebControls; namespaceMSPress.ServerControls{ [ Designer(typeof(MSPress.ServerControls.Design.MyPanelDesigner) ParseChildren(false), PersistChildren(true) ] publicclassMyPanel:WebControl{ publicMyPanel():base(HtmlTextWriterTag.Div){ } [ Bindable(true), Category("Appearance"), DefaultValue(""), Editor(typeof(ImageUrlEditor),typeof(UITypeEditor)), Description("TheURLforthebackgroundimage") ] publicvirtualstringBackImageUrl{ get{ if(ControlStyleCreated){ return((MyPanelStyle)ControlStyle).BackImageUrl; } returnString.Empty; } set{ ((MyPanelStyle)ControlStyle).BackImageUrl=value; } } [ Bindable(true), Category("Layout"), DefaultValue(HorizontalAlign.NotSet), Description("TheHorizontalAlignment") ] publicvirtualHorizontalAlignHorizontalAlign{ get{ if(ControlStyleCreated){ return ((MyPanelStyle)ControlStyle).HorizontalAlign; } returnHorizontalAlign.NotSet; } set{ ((MyPanelStyle)ControlStyle).HorizontalAlign=value; } } [ Bindable(true), Category("Layout"), DefaultValue(true), Description("Specifieswhethercontentwrapswithinthepanel") ] publicvirtualboolWrap{ get{ if(ControlStyleCreated){ return((MyPanelStyle)ControlStyle).Wrap; } returntrue; } set{ ((MyPanelStyle)ControlStyle).Wrap=value; } } protectedoverrideStyleCreateControlStyle(){ returnnewMyPanelStyle(ViewState); } } } The MyPanel control overrides the CreateControlStyle method to return an instance of the MyPanelStyle style. This indirectly causes the returned MyPanelStyle instance to be assigned to the ControlStyle property. As we described in the "Styles Overview" section, the ControlStyle property is read-only and is indirectly set when its accessor invokes the CreateControlStyle method. Notice that CreateControlStyle passes the MyPanel control's ViewState into the constructor of MyPanelStyle . When you create a new style for your control in CreateControlStyle , you must pass your control's ViewState into the Style constructor so that the style object uses the same StateBag as your control. If you are overriding existing style properties, you should not create a new Style instance, as we saw in the Spreadsheet example in the previous section. MyPanel also defines new style properties by exposing properties of MyPanelStyle as its own top-level properties. As we described in the previous paragraph, the type of the ControlStyle property is MyPanelStyle . MyPanel also has a designer that is associated with it via the DesignerAttribute . The code for the designer is included with the book's sample files. Designers are explained in Chapter 15, "Design-Time Functionality." We'll now implement the MyPanelStyle class that we used in the MyPanel control. These are the key steps in implementing a typed style:
Listing 11-4 contains the code for the MyPanelStyle typed style. Listing 11-4 MyPanelStyle.csusingSystem; usingSystem.ComponentModel; usingSystem.Web.UI; usingSystem.Web.UI.WebControls; namespaceMSPress.ServerControls{ publicclassMyPanelStyle:Style{ internalconstintPROP_BACKIMAGEURL=1; internalconstintPROP_HORIZONTALALIGN=2; internalconstintPROP_WRAP=3; publicMyPanelStyle(){ } publicMyPanelStyle(StateBagbag):base(bag){ } [ Bindable(true), Category("Appearance"), DefaultValue(""), Description("ThebackgroundimageofthePanel"), NotifyParentProperty(true) ] publicvirtualstringBackImageUrl{ get{ if(IsSet(PROP_BACKIMAGEURL)){ return(string)ViewState["BackImageUrl"]; } returnString.Empty; } set{ ViewState["BackImageUrl"]=value; } } [ Bindable(true), Category("Layout"), DefaultValue(HorizontalAlign.NotSet), Description("ThehorizontalalignmentofthePanel'scontents"), NotifyParentProperty(true) ] publicvirtualHorizontalAlignHorizontalAlign{ get{ if(IsSet(PROP_HORIZONTALALIGN)){ return (HorizontalAlign)ViewState["HorizontalAlign"]; } returnHorizontalAlign.NotSet; } set{ if(value<HorizontalAlign.NotSet value>HorizontalAlign.Justify){ thrownewArgumentOutOfRangeException("value"); } ViewState["HorizontalAlign"]=value; } } protectednewinternalboolIsEmpty{ get{ returnbase.IsEmpty&& !IsSet(PROP_BACKIMAGEURL)&& !IsSet(PROP_HORIZONTALALIGN)&& !IsSet(PROP_WRAP); } } [ Bindable(true), Category("Layout"), DefaultValue(true), Description("WhetherthePanel'scontentscanwraparound"), NotifyParentProperty(true) ] publicvirtualboolWrap{ get{ if(IsSet(PROP_WRAP)){ return(bool)ViewState["Wrap"]; } returntrue; } set{ ViewState["Wrap"]=value; } } publicoverridevoidAddAttributesToRender(HtmlTextWriterwriter,WebControlowner){ base.AddAttributesToRender(writer,owner); if(IsSet(PROP_BACKIMAGEURL)){ strings=BackImageUrl; if(s.Length>0){ if(owner!=null){ s=owner.ResolveUrl(s); } writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url("+s+")"); } } if(IsSet(PROP_HORIZONTALALIGN)){ HorizontalAlignhAlign=HorizontalAlign; if(hAlign!=HorizontalAlign.NotSet){ TypeConverterhac= TypeDescriptor.GetConverter(typeof(HorizontalAlign)); writer.AddAttribute(HtmlTextWriterAttribute.Align, hac.ConvertToInvariantString(hAlign)); } } if(IsSet(PROP_WRAP)){ boolwrap=Wrap; if(!Wrap) writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "nowrap"); } } publicoverridevoidCopyFrom(Styles){ if(s!=null){ base.CopyFrom(s); if(sisMyPanelStyle){ MyPanelStylemps=(MyPanelStyle)s; if(!mps.IsEmpty){ if(mps.IsSet(PROP_BACKIMAGEURL)) this.BackImageUrl=mps.BackImageUrl; if(mps.IsSet(PROP_HORIZONTALALIGN)) this.HorizontalAlign=mps.HorizontalAlign; if(mps.IsSet(PROP_WRAP)) this.Wrap=mps.Wrap; } } } } internalboolIsSet(intpropNumber){ stringkey=null; switch(propNumber){ casePROP_BACKIMAGEURL: key="BackImageUrl"; break; casePROP_HORIZONTALALIGN: key="HorizontalAlign"; break; casePROP_WRAP: key="Wrap"; break; }if(key!=null){ returnViewState[key]!=null; } returnfalse; } publicoverridevoidMergeWith(Styles){ if(s!=null){ if(IsEmpty){ //Mergingwithanemptystyleisequivalenttocopying, //whichismoreefficient. CopyFrom(s); return; } base.MergeWith(s); if(sisMyPanelStyle){ MyPanelStylemps=(MyPanelStyle)s; if(!mps.IsEmpty){ if(mps.IsSet(PROP_BACKIMAGEURL)&& !this.IsSet(PROP_BACKIMAGEURL)) this.BackImageUrl=mps.BackImageUrl; if(mps.IsSet(PROP_HORIZONTALALIGN)&& !this.IsSet(PROP_HORIZONTALALIGN)) this.HorizontalAlign=mps.HorizontalAlign; if(mps.IsSet(PROP_WRAP)&& !this.IsSet(PROP_WRAP)) this.Wrap=mps.Wrap; } } } } publicoverridevoidReset(){ base.Reset(); if(IsEmpty){ return; }if(IsSet(PROP_BACKIMAGEURL)) ViewState.Remove("BackImageUrl"); if(IsSet(PROP_HORIZONTALALIGN)) ViewState.Remove("HorizontalAlign"); if(IsSet(PROP_WRAP)) ViewState.Remove("Wrap"); } } } The MyPanelStyle typed style performs the following tasks :
|