Defining the PageCommand EventThe Pager control exposes a custom PageCommand event to let its client know whether it is moving in the left or right direction. The PageDirection enumeration provides a finite way to specify this in code:public enum PageDirection { Left = 0, Right = 1 }The PageCommandEventArgs class uses this enumeration as the data type for its Direction property exposed as part of an EventArgs replacement for the PageCommand delegate. The complete PageCommand event–related code is grouped in the PageCommand class file shown in Listing 5-14.Listing 5-14. The PageCommand Class Fileusing System;namespace ControlsBookLib.Ch05{ public enum PageDirection { Left = 0, Right = 1 } public delegate void PageCommandEventHandler(object o, PageCommandEventArgs pce); public class PageCommandEventArgs { public PageCommandEventArgs(PageDirection direction) { this.direction = direction; } PageDirection direction; public PageDirection Direction { get { return direction; } } }}Exposing the PageCommand Event from the Pager ControlThe Pager control uses the PageCommandEventHandler delegate to declare its eventhandling code. As with the SuperButton, we use the Events property technique for handling delegate registration:private static readonly object PageCommandKey = new object();public event PageCommandEventHandler PageCommand { add { Events.AddHandler(PageCommandKey, value); } remove { Events.RemoveHandler(PageCommandKey, value); } }We also add an OnPageCommand method to raise the event. This method uses the custom PageCommandEventArgs class we defined earlier to invoke the PageCommandEventHandler delegate:protected virtual void OnPageCommand(PageCommandEventArgs pce){PageCommandEventHandler pageCommandEventDelegate =(PageCommandEventHandler) Events[PageCommandEvent];if (pageCommandEventDelegate != null){pageCommandEventDelegate(this, pce);}}OnPageCommand is the last bit of code required to raise events associated with the PageCommand event type. The next task is to capture the bubbled Command events and turn them into PageCommand events.Capturing the Bubbles via OnBubbleEventThe OnBubbleEvent method inherited from System.Web.UI.Control is the counterpart to the RaiseBubbleEvent method used inside the SuperButton control. It allows a control to hook into the stream of bubbled events from child controls and process them accordingly:protected override bool OnBubbleEvent(object source, EventArgs e);The method definition for OnBubbleEvent specifies the ubiquitous System.EventHandler method signature, with one difference. It takes an object reference and an EventArgs reference, but returns a bool. The bool return value indicates whether or not the control has processed the bubble event. A value of false indicates that the bubble event should continue bubbling up the control hierarchy; a value of true indicates a desire to stop the event in its tracks because it has been handled. If a control does not implement OnBubbleEvent, the default implementation passes the event on up to parent controls. The Pager control implements its OnBubbleEvent as shown in Listing 5-15.Listing 5-15. The Pager Implementation of OnBubbleEventprotected override bool OnBubbleEvent(object source, EventArgs e){bool result = false;CommandEventArgs ce = e as CommandEventArgs;if (ce != null){if (ce.CommandName.Equals("Page")){PageDirection direction;if (ce.CommandArgument.Equals("Right"))direction = PageDirection.Right;elsedirection = PageDirection.Left;PageCommandEventArgs pce =new PageCommandEventArgs(direction);OnPageCommand(pce);result = true;}}return result;}The result variable holds the return value of OnBubbleEvent for the Pager control. It is set to false, assuming failure until success. The first check is to cast the EventArgs reference to ensure we receive a Command event of the proper type. The code performs this check using the as keyword in C# to cast the reference to the desired type, which returns null if the cast fails. If the type cast succeeds, the next check is to ensure the proper CommandName is set to "Page". After the checks pass, the OnBubbleEvent code can create a PageCommandEventArgs class and set the Direction property according to the CommandArgument value. The final task is to raise the PageCommand event by calling OnPageCommand. Finally, the function returns the value of result to tell the ASP.NET framework whether or not the event was handled.The INamingContainer InterfaceWhen a composite control builds up its child control tree, it sets each control's identification via the ID property. For example, the Pager control sets the left SuperButton child control ID property value in the following single line of code:buttonLeft.ID = "buttonLeft";
The problem with using just the ID value to uniquely identify child controls is that multiple Pager controls could be used on a Web Form, and the emitted button or hyperlink ID values would conflict. To protect against name collisions, each composite control creates a unique namespace that prefixes the ID of a control with the parent control's ID and a colon. The INamingContainer interface tells ASP.NET to do this. INamingContainer is a marker interface (i.e., an interface without any defined methods) used by ASP.NET to identify the parent in a composite control to ensure unique names or IDs for child controls as they are dynamically created during the page-rendering process.Implementing the INamingContainer interface in the Pager server control activates this mechanism, causing ASP.NET to prefix the ID of a control with the parent control's ID and a colon. The previous left button in a Pager control named "pagerbtn" would therefore have an ID value of "buttonLeft" but a UniqueID value of "pagerbtn:buttonLeft". Listing 5-16 contains the full code listing for the Pager control.Listing 5-16. The Pager Control Class Fileusing System;using System.ComponentModel;using System.Web.UI;using System.Web.UI.WebControls;using ControlsBookLib.Ch12.Design;namespace ControlsBookLib.Ch05{[ToolboxData("<{0}:Pagerrunat=server></{0}:Pager>"),Designer(typeof(CompCntrlDesigner))] public class Pager : Control, INamingContainer { private static readonly object PageCommandKey = new object(); public event PageCommandEventHandler PageCommand { add { Events.AddHandler(PageCommandKey, value); } remove { Events.RemoveHandler(PageCommandKey, value); } } protected virtual void OnPageCommand(PageCommandEventArgs pce) { PageCommandEventHandler pageCommandEventDelegate = (PageCommandEventHandler)Events[PageCommandKey]; if (pageCommandEventDelegate != null) { pageCommandEventDelegate(this, pce); } } protected override bool OnBubbleEvent(object source, EventArgs e) { bool result = false; CommandEventArgs ce = e as CommandEventArgs; if (ce != null) { if (ce.CommandName.Equals("Page")) { PageDirection direction; if (ce.CommandArgument.Equals("Right")) direction = PageDirection.Right; else direction = PageDirection.Left; PageCommandEventArgs pce = new PageCommandEventArgs(direction); OnPageCommand(pce); result = true; } } return result; } public virtual ButtonDisplay Display { get { EnsureChildControls(); return buttonLeft.Display; } set { EnsureChildControls(); buttonLeft.Display = value; buttonRight.Display = value; } } protected override void CreateChildControls() { Controls.Clear(); CreateChildControlHierarchy(); } public override ControlCollection Controls { get { EnsureChildControls(); return base.Controls; } } private SuperButton buttonLeft; private SuperButton buttonRight; private void CreateChildControlHierarchy() { LiteralControl tableStart = new LiteralControl("<table border=1><tr><td>"); Controls.Add(tableStart); buttonLeft = new SuperButton(); buttonLeft.ID = "buttonLeft"; if (Context != null) { buttonLeft.Text = Context.Server.HtmlEncode("<") + " Left"; } else { buttonLeft.Text = "< Left"; } buttonLeft.CommandName = "Page"; buttonLeft.CommandArgument = "Left"; Controls.Add(buttonLeft); LiteralControl spacer = new LiteralControl(" "); Controls.Add(spacer); buttonRight = new SuperButton(); buttonRight.ID = "buttonRight"; buttonRight.Display = Display; if (Context != null) { buttonRight.Text = "Right " + Context.Server.HtmlEncode(">"); } else { buttonRight.Text = "Right >"; } buttonRight.CommandName = "Page"; buttonRight.CommandArgument = "Right"; Controls.Add(buttonRight); LiteralControl tableEnd = new LiteralControl("</td></tr></table>"); Controls.Add(tableEnd); } }}