Implementing IStateManager in a Custom TypeIn this section, we'll define a HotSpot class that takes responsibility for managing its own state by implementing the IStateManager interface. The HotSpot class will serve as the base class for types that correspond to circular and rectangular hot spot regions in an image map. In the ImageMap example in the next section, we will use the HotSpot type to define a collection of hot spots in an image map. Listing 10-11 contains the code for the abstract HotSpot class. Later in this section, you'll see concrete classes that derive from HotSpot , including CircleHotSpot and RectangleHotSpot . Listing 10-11 HotSpot.csusingSystem; usingSystem.Collections; usingSystem.ComponentModel; usingSystem.Web.UI; namespaceMSPress.ServerControls{ [ TypeConverter(typeof(ExpandableObjectConverter)) ] publicabstractclassHotSpot:IStateManager{ privatebool_isTrackingViewState; privateStateBag_viewState; protectedHotSpot():this(String.Empty,String.Empty){ } protectedHotSpot(stringaction,stringtoolTip){ this.Action=action; this.ToolTip=toolTip; } [ Category("Behavior"), DefaultValue(""), Description("URLfornavigationorargumentforpostbackevent"), NotifyParentProperty(true), ] publicStringAction{ get{ stringaction=(string)(ViewState["Action"]); return(action==null)?String.Empty:action; } set{ ViewState["Action"]=value; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] publicabstractMapShapeShape{ get; } [ Category("Behavior"), DefaultValue(""), Description("Tooltiptodisplayoverthehotspot"), NotifyParentProperty(true) ] publicStringToolTip{ get{ stringtip=(string)ViewState["ToolTip"]; return(tip==null)?String.Empty:tip; } set{ ViewState["ToolTip"]=value; } } protectedabstractboolShapeCreated{ get; } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] protectedStateBagViewState{ get{ if(_viewState==null){ _viewState=newStateBag(false); if(_isTrackingViewState){ ((IStateManager)_viewState).TrackViewState(); } } return_viewState; } } internalvoidSetDirty(){ if(_viewState!=null){ ICollectionKeys=_viewState.Keys; foreach(stringkeyinKeys){ _viewState.SetItemDirty(key,true); } } } publicoverridestringToString(){ returnGetType().Name; } #regionIStatemanagerimplementation boolIStateManager.IsTrackingViewState{ get{ return_isTrackingViewState; } } voidIStateManager.LoadViewState(objectsavedState){ if(savedState!=null){ ((IStateManager)ViewState).LoadViewState(savedState); stringsavedShape=(string)ViewState["Shape"]; if(savedShape!=null){ Shape.LoadFromString(savedShape); } } } objectIStateManager.SaveViewState(){ if(ShapeCreated){ stringcurrentShape=Shape.SaveToString(); stringinitialShape=(string)ViewState["Shape"]; if(currentShape.Equals(initialShape)==false){ ViewState["Shape"]=currentShape; } } if(_viewState!=null){ return((IStateManager)_viewState).SaveViewState(); } returnnull; } voidIStateManager.TrackViewState(){ if(ShapeCreated){ ViewState["Shape"]=Shape.SaveToString(); } _isTrackingViewState=true; if(_viewState!=null){ ((IStateManager)_viewState).TrackViewState(); } } #endregion } } HotSpot defines three public properties:
The most interesting feature of HotSpot is its state management. HotSpot defines a protected ViewState property of type StateBag and delegates state management to it. This is analogous to the default implementation of state management in the Control class and is a convenient technique for implementing state management if you implement a custom type that has several properties. Note, however, that HotSpot is not a Control ; it does not derive from Control or share any other characteristics of the Control class. HotSpot implements its simple properties (such as Action and ToolTip ) as read-write properties and stores them directly in its ViewState dictionary. HotSpot implements its complex Shape property as a read-only property and performs state management for Shape in its implementation of the IState Manager interface. In the TrackViewState method, HotSpot saves the string representation of the Shape property in its ViewState dictionary and then invokes TrackViewState on its ViewState property. The order of these operations is important and ensures that the initial state of the Shap e property is not marked dirty in ViewState . In the SaveViewState method, HotSpot checks whether the value of its Shape property differs from the initial state stored in ViewState . If the value does differ , HotSpot stores the new state in ViewState , which ViewState now marks as dirty. HotSpot next invokes SaveViewState on its ViewState property. In the LoadViewState method, HotSpot first invokes the LoadViewState method on its ViewState property to load state into ViewState . HotSpot then looks up the value of its Shape property in ViewState , and if it finds any saved state, HotSpot uses that saved state to load the state of the Shape property. Listing 10-12 shows the code for the CircleHotSpot class. The code for the RectangleHotSpot class is similar and is provided in the book's sample files. Listing 10-12 CircleHotSpot.csusingSystem; usingSystem.ComponentModel; usingSystem.Web.UI; namespaceMSPress.ServerControls{ publicclassCircleHotSpot:HotSpot{ privateMapCircle_circle; publicCircleHotSpot(): this(0,0,0,String.Empty,String.Empty){ } publicCircleHotSpot(intx,inty,intradius,stringaction, stringtoolTip):base(action,toolTip){ if((x!=0)(y!=0)(radius!=0)){ Origin=newMapPoint(x,y); Radius=radius; } } [ Category("Shape"), DefaultValue(typeof(MapPoint),""), Description("Theoriginofthecircularhotspot"), NotifyParentProperty(true) ] publicMapPointOrigin{ get{ return((MapCircle)Shape).Origin; } set{ if(value==null){ thrownewArgumentNullException("value"); } ((MapCircle)Shape).Origin=value; } } [ Category("Shape"), DefaultValue(0), Description("Theradiusofthecircularhotspot"), NotifyParentProperty(true) ] publicintRadius{ get{ return((MapCircle)Shape).Radius; } set{ if(value<0){ thrownewArgumentOutOfRangeException("value"); } ((MapCircle)Shape).Radius=value; } } publicoverrideMapShapeShape{ get{ if(_circle==null){ _circle=newMapCircle(); } return_circle; } } protectedoverrideboolShapeCreated{ get{ return(_circle!=null); } } } } |