Putting It Together ”The PageTracker ExampleWe'll now develop a control ( PageTracker ) that brings together the different ideas we've introduced in this chapter. The PageTracker control tracks the number of hits a page receives per application or per session and computes the round-trip time for a page. PageTracker exposes simple properties of several different types: PageTrackingMode (a custom enumeration type that we will define) and three built-in types: Int32 , String, and TimeSpan . PageTracker uses three different mechanisms for state management ( ViewState , Session , and Application ) and applies design-time attributes for designer support. We'll show the code for PageTracker first and then describe the details. Listing 7-3 contains the code for the PageTrackingMode enumeration that is used by PageTracker. Listing 7-3 PageTrackingMode.csusingSystem; namespaceMSPress.ServerControls{ publicenumPageTrackingMode{ ByApplication, BySession, ByTripTime } } Listing 7-4 contains the code for the PageTracker control. Listing 7-4 PageTracker.csusingSystem; usingSystem.ComponentModel; usingSystem.Web.UI; usingSystem.Web.UI.WebControls; namespaceMSPress.ServerControls{ [ DefaultProperty("TrackingMode"), ] publicclassPageTracker:WebControl{ privateTimeSpan_tripTime; [ Category("Appearance"), DefaultValue("{0}"), Description("Theformattingstringusedtodisplaythe " + "valuebeingtracked.") ] publicvirtualstringFormatString{ get{ strings=(string)ViewState["FormatString"]; return((s==null)? "{0}" :s); } set{ ViewState["FormatString"]=value; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] publicintHits{ get{ PageTrackingModemode=TrackingMode; objecto=null; if(mode==PageTrackingMode.ByApplication){ o=Page.Application[HitsKey]; } elseif(mode==PageTrackingMode.BySession){ o=Page.Session[HitsKey]; } else{ thrownewNotSupportedException("Hitsisonly " + "supportedwhenTrackingModeis " + "PageTrackingMode.ByApplicationor " + "PageTrackingMode.BySession"); } return((o==null)?0:(int)o); } } [ Category("Behavior"), DefaultValue(PageTrackingMode.ByApplication), Description("Thetypeoftrackingtoperform.") ] publicvirtualPageTrackingModeTrackingMode{ get{ objectmode=ViewState["TrackingMode"]; return((mode==null)? PageTrackingMode.ByApplication: (PageTrackingMode)mode); } set{ if(value<PageTrackingMode.ByApplication value>PageTrackingMode.ByTripTime){ thrownewArgumentOutOfRangeException("value"); } ViewState["TrackingMode"]=value; //Performcleanupoftheoldstorage. //WehavetocheckthatthePageisnotnull //becausethecontrolcouldbeinitialized //beforeitisaddedtothecontroltree. //NotethattheApplicationandSession //objectsarenotavailableinthedesigner. switch(TrackingMode){ casePageTrackingMode.ByApplication: if(Page!=null&&Page.Application!=null){ Page.Application.Remove(HitsKey); } break; casePageTrackingMode.BySession: if(Page!=null&&Page.Session!=null){ Page.Session.Remove(HitsKey); } break; casePageTrackingMode.ByTripTime: ViewState.Remove("TimeStamp"); break; } } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] publicTimeSpanTripTime{ get{ if(TrackingMode!=PageTrackingMode.ByTripTime){ thrownewNotSupportedException("TripTimeis " + "onlysupportedwhenTrackingModeis " + "PageTrackingMode.ByTripTime"); } return_tripTime; } } protectedoverrideHtmlTextWriterTagTagKey{ get{ returnHtmlTextWriterTag.Div; } } privatestringHitsKey{ get{ //CreateauniqueHitsKeyforthepageforkeying //inhitsperpageintheApplicationandSession //objects. returnPage.GetType().FullName; } } protectedoverridevoidOnLoad(EventArgse){ base.OnLoad(e); switch(TrackingMode){ casePageTrackingMode.ByApplication: //UpdatethepagehitsintheApplication //object.Thisoperationneedsalockbecause //multipleuserscouldaccessapageatthesame //time. lock(Page.GetType()){ Page.Application[HitsKey]=Hits+1; } break; casePageTrackingMode.BySession: Page.Session[HitsKey]=Hits+1; break; casePageTrackingMode.ByTripTime: //Getthetimestampforthepreviousrequest //fromViewStateandcomputethedifference //betweentheprevious //andcurrenttimestamp. objecttimeStamp=ViewState["TimeStamp"]; DateTimerequestTime=Context.Timestamp; if(timeStamp==null){ _tripTime=TimeSpan.Zero; this.Visible=false; } else{ this.Visible=true; _tripTime= (requestTime-(DateTime)timeStamp); } ViewState["TimeStamp"]=requestTime; break; } } protectedoverridevoidRenderContents(HtmlTextWriterwriter){ switch(TrackingMode){ casePageTrackingMode.ByApplication: casePageTrackingMode.BySession: writer.Write(FormatString,Hits); break; casePageTrackingMode.ByTripTime: writer.Write(FormatString,TripTime.TotalSeconds); break; } } } } The PageTracker control performs the following tasks :
Figure 7-3 shows the properties of PageTracker in the property browser of Microsoft Visual Studio .NET, sorted by category. Among the properties displayed, we defined FormatString and TrackingMode , while PageTracker inherited the other properties from WebControl . The Hits and TripTime read-only properties are not displayed because we marked those properties as Browsable(false) . The HitsKey property does not appear either because it is private. The property browser never displays properties that are not public. Notice that the TrackingMode property displays a drop-down list that shows the values of the PageTrackingMode enumeration. We did not do any extra work to provide this functionality; the .NET Framework enables it by automatically associating an EnumConverter with an enumeration. The descriptive string at the bottom of the property browser displays the text in the DescriptionAttribute that we applied to the TrackingMode property. Figure 7-3. The PageTracker properties displayed in Visual Studio .NET
Page That Uses the PageTracker ControlThe ASP.NET page shown in Listing 7-5 has four instances of the PageTracker control. The first three instances correspond to the three tracking options specified in the PageTrackingMode enumeration. In the last instance of PageTracker , view state is disabled by setting EnableViewState="false" . As with enumerated properties in standard ASP.NET controls, the TrackingMode property is set declaratively by specifying the value of the PageTrackingMode enumeration without the qualifying PageTrackingMode type name . Listing 7-5 PageTrackerTest.aspx<%@PageLanguage="C#" %> <%@RegisterTagPrefix="msp" Namespace="MSPress.ServerControls" Assembly="MSPress.ServerControls" %> <html> <body> <formrunat="server"> <br> <asp:ButtonText="Submit" Runat="server" id="Button1" /> <br><br> <msp:PageTrackerid="Pagetracker1" TrackingMode="ByApplication" FormatString="PageHits:{0}" Width="200px" Font-Name="Verdana" Font-Size="14pt" ForeColor="Black" BackColor="#E0E0E0" runat="server" BorderStyle="None" BorderColor="White" /> <br> <msp:PageTrackerid="Pagetracker2" TrackingMode="BySession" FormatString="SessionHits:{0}" Width="200px" Font-Name="Verdana" Font-Size="14pt" ForeColor="Black" BackColor="#E0E0E0" runat="server" /> <br> <msp:PageTrackerid="Pagetracker3" TrackingMode="ByTripTime" FormatString="Round-TripTime:{0:F2}seconds" Width="350px" Font-Name="Verdana" Font-Size="14pt" ForeColor="Black" BackColor="#E0E0E0" runat="server" /> <br> <msp:PageTrackerid="Pagetracker4" EnableViewState="false" TrackingMode="ByTripTime" Width="300px" Font-Name="Verdana" Font-Size="14pt" ForeColor="Black" BackColor="#E0E0E0" runat="server" /> <br> <br> </form> </body> </html> Open PageTrackerTest.aspx in your browser. Press the Submit button several times. Then close the browser window and open another browser window. Access PageTrackerTest.aspx again, and submit the page several times. The output in your browser should be similar to that shown in Figure 7-4. Note that the last instance of PageTracker is not displayed because when view state is disabled, the TripTime value cannot be computed. In that case, PageTracker sets its Visible property to false so that the control is not rendered. Figure 7-4. PageTrackerTest.aspx accessed in a browser
|