Recipe 6.5. Using the Control State with Custom Controls


Problem

You want to allow users to disable the ViewState for your custom control but you do not want to lose functionality when they do. In other words, when a developer disables the ViewState for the page or your custom control, you want to be able to maintain critical state information for your control.

Solution

Create a custom control like the one described in Recipe 6.3, register the control with the Page indicating the control requires use of the Control State, override the SaveControlState method to save the critical information for your control in the Control State, and override the LoadControlState method to reload the critical information from the Control State on postback.

Use the .NET language of your choice to:

  1. Create a class that inherits from the WebControl class in the System.Web.UI.WebControls namespace.

  2. Implement state support as described in Recipe 6.3.

  3. In the control Init method, register the control with the Page indicating the control requires use of the Control State.

  4. Override the SaveControlState method to save the critical information for your control in the Control State.

  5. Override the LoadControlState method to reload the control information from the Control State on postback.

To use the custom control in an ASP.NET page:

  1. Register the assembly containing the control.

  2. Insert the tag for the custom control anywhere in the page and set the attributes appropriately.

Examples 6-15 and 6-16 show the VB and C# class files for a custom control that maintains its critical state information in the Control State. Examples 6-17, Examples 6-18 through 6-19 show how to use the custom control in an ASP.NET page.

Discussion

Custom controls developed in ASP.NET 1.x had only one way to save critical state information. The only option available was to use the ViewState, which worked well until a developer disabled the ViewState for the page or your custom control. When he disabled the ViewState, the state information needed by your control was unavailable at postback, generally resulting in loss of functionality for your control.

ASP.NET 2.0 provides another method of saving the critical state information. This method uses the Control State to save the state information in the page in the same manner as the ViewState, but the Control State cannot be disabled. Saving the critical state information in the Control State provides the ability for the developer to improve performance by disabling the ViewState without compromising the functionality of your control.

The Control State should be used only for small amounts of critical state information that your control must have upon postback. The information you place in the Control State is stored in the rendered page as a hidden input control (as part of same hidden control that stores the ViewState). This results in the information being sent to the browser with the page request as well as being sent back to the server upon postback, which will affect performance if significant amounts of data are stored in the Control State.


To store state information in the Control State, you will need to implement a control similar to the one described in Recipe 6.3. One modification is required to the control from what is described in Recipe 6.3. You need to remove any property storage from the ViewState, as shown below for the text property:

 

Public Property text() As String Get Return (mText) End Get Set(ByVal Value As String) mText = Value End Set End Property 'text

public String text { get { return (mText); } set { mText = value; } } // text

Next, you need to register the control with the page to inform the page that the Control State is required for your control. The registration is normally performed in the Init event handler for the control.

 

Private Sub CH06CustomControlWithStateVB1_Init(ByVal sender As Object, _ ByVal e As EventArgs) _ Handles Me.Init Page.RegisterRequiresControlState(Me) End Sub 'CH06CustomControlWithStateVB1_Init

protected override void OnInit(EventArgs e) { base.OnInit(e); Page.RegisterRequiresControlState(this); }

Then you need to override the SaveControlState method to save the critical state information for your control in the Control State. Storing data in the Control State requires verifying that you have data to store and checking to see if other data is being stored in the Control State, as shown in Examples 6-15 (VB) and 6-16 (C#).

All data stored in the Control State is managed by the Page and is stored as object Pairs. When storing your data in the Control State, you must check for data stored in the Control State and add your data accordingly. Failure to handle previously added data or incorrectly adding your data will affect the operation of the page and other controls on the page.


Finally, you need to override the LoadControlState method to reload your critical state information upon postback. This requires checking to see if any data is available and, if so, evaluating its type. If the data type is a Pair, you must pass the First property of the pair to the base class and use the Second property as your data. If the data type is not a Pair, you will need to verify the data type is the type you are expecting for your control, as shown in Examples 6-15 (VB) and 6-16 (C#).

When the state data provided to the LoadControlState method is of type Pair, you must pass the data to the base class. Failure to do so will result in incorrect operation of the page or other controls on the page.


The Control State is a valuable asset for custom control development. With it, you can ensure critical state information is available while providing the developer the ability to enhance performance by disabling the ViewState.

See Also

Recipes 6.3 and 19.1

Example 6-15. Custom control using the control state (.vb)

 Option Explicit On Option Strict On Imports System Imports System.Drawing Namespace ASPNetCookbook.VBExamples   ''' <summary>   ''' This class provides a custom control that maintains state through   ''' postbacks to the server using the page control state.   ''' </summary>   Public Class CH06CustomControlWithStateVB3     Inherits WebControl Implements IPostBackDataHandler 'define an event to be raised if the text changes Public Event TextChanged As EventHandler 'private copies of attribute data Private mLabelText As String = "Label: " Private mTextColor As Color = Color.Black Private mTextboxWidth As Integer = 3 Private mText As String = Nothing '''*********************************************************************** ''' <summary> ''' This property provides the ability to set the text of of the label ''' in the control ''' </summary> Public Property labelText( ) As String   Get Return (mLabelText)   End Get   Set(ByVal Value As String) mLabelText = Value   End Set End Property 'labelText '''*********************************************************************** ''' <summary> ''' This property provides the ability to set the color of the text ''' in the control ''' </summary> Public Property textColor( ) As Color   Get Return (mTextColor)   End Get   Set(ByVal Value As Color) mTextColor = Value   End Set End Property 'textColor '''*********************************************************************** ''' <summary> ''' This property provides the ability to set the width of the text box ''' in the control ''' </summary> Public Property textboxWidth( ) As Integer Get Return (mTextboxWidth) End Get Set(ByVal Value As Integer) mTextboxWidth = Value End Set End Property 'textboxWidth '''*********************************************************************** ''' <summary> ''' This property provides the ability to set the HTML tag that is used ''' as the container for the control ''' </summary> Protected Overrides ReadOnly Property TagKey( ) As HtmlTextWriterTag Get Return (HtmlTextWriterTag.Div) End Get End Property 'TagKey '''*********************************************************************** ''' <summary> ''' This property provides the ability to get/set the text in the text box ''' in the control. ''' </summary> Public Property text( ) As String Get Return (mText) End Get Set(ByVal Value As String) mText = Value End Set End Property 'text '''*********************************************************************** ''' <summary> ''' This routine renders the HTML output of the control contents ''' </summary> ''' ''' <param name="writer">Set to the HtmlTextWriter to use to output the ''' rendered HTML for the control ''' </param> Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)   'output label within a font tag   writer.AddAttribute("color", _    ColorTranslator.ToHtml(textColor))   writer.RenderBeginTag(HtmlTextWriterTag.Font)   writer.Write(labelText)   writer.RenderEndTag( )   'output input control   writer.AddAttribute(HtmlTextWriterAttribute.Type, _   "text")   writer.AddAttribute(HtmlTextWriterAttribute.Size, _   textboxWidth.ToString( ))   'output name attribute to identify data on postback   writer.AddAttribute(HtmlTextWriterAttribute.Name, _   Me.UniqueID)   'output value attribute only if value exists   If (Not IsNothing(text)) Then writer.AddAttribute(HtmlTextWriterAttribute.Value, _ text)   End If   writer.RenderBeginTag(HtmlTextWriterTag.Input)   writer.RenderEndTag( ) End Sub 'RenderContents '''*********************************************************************** ''' <summary> ''' This routine processes data posted back from the client ''' </summary> ''' ''' <param name="postDataKey">The key identifier for the control</param> ''' <param name="postCollection">The collection of all incoming name ''' values</param> ''' ''' <returns>set true if the server control's state changes as a result ''' of the post back; otherwise false ''' </returns> Public Overridable Function LoadPostData(ByVal postDataKey As String, _ ByVal postCollection As NameValueCollection) As Boolean _    Implements IPostBackDataHandler.LoadPostData   Dim dataChanged As Boolean = False   Dim postbackValue As String   'check to see if the data changed   postbackValue = postCollection(postDataKey)   If (Not postbackValue.Equals(text)) Then     dataChanged = True   End If   'set the value of the text property from the postback data   text = postbackValue   Return (dataChanged) End Function 'LoadPostData '''*********************************************************************** ''' <summary> ''' This routine processes data changed events as a result of the postback ''' </summary> Public Overridable Sub RaisePostDataChangedEvent( ) _    Implements IPostBackDataHandler.RaisePostDataChangedEvent    RaiseEvent TextChanged(Me, EventArgs.Empty) End Sub 'RaisePostDataChangedEvent '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the control init event. ''' It is responsible for registering with the page to indicate this ''' control requires the control state ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub CH06CustomControlWithStateVB1_Init(ByVal sender As Object, _   ByVal e As EventArgs) _ Handles Me.Init   Page.RegisterRequiresControlState(Me) End Sub 'CH06CustomControlWithStateVB1_Init '''*********************************************************************** ''' <summary> ''' This routine provides the ability to restore the data from the control ''' state that was previously saved by the SaveControlState method ''' </summary> ''' ''' <param name="state">Set to the control state to restore</param> Protected Overrides Sub LoadControlState(ByVal state As Object)    Dim statePair As Pair 'check to see if there is any control state to restore If (Not state Is Nothing) Then 'check to see if the state information contain base data     If (TypeOf state Is Pair) Then    'state contains control state information for the base class so    'extract it and pass it along to the base class    statePair = CType(state, Pair)    MyBase.LoadControlState(statePair.First)    'get the state information for this object    text = CStr(statePair.Second) Else 'state contains only simple data so check to see if it is 'applicable to this object If (TypeOf state Is String) Then 'get the state information for this object text = CStr(state) Else 'pass state information on to base object MyBase.LoadControlState(state) End If  End If        End If End Sub 'LoadControlState '''*********************************************************************** ''' <summary> ''' This routine provides the ability to save information in the control ''' state ''' </summary> ''' ''' <returns>An object containing the information to store in the control ''' state ''' </returns> Protected Overrides Function SaveControlState( ) As Object Dim baseControlState As Object Dim returnValue As Object = Nothing baseControlState = MyBase.SaveControlState( ) If (text Is Nothing) Then returnValue = baseControlState Else If (baseControlState Is Nothing) Then returnValue = text Else returnValue = New Pair(baseControlState, _ text) End If End If Return (returnValue) End Function 'SaveControlState End Class 'CH06CustomControlWithStateVB3   End Namespace 

Example 6-16. Custom control using the control state (.cs)

 using System; using System.Collections.Specialized; using System.Drawing; using System.Web.UI; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples {   /// <summary>   /// This class provides a custom control that maintains state through   /// postbacks to the server and raises an event when the entered   /// data changes.   /// </summary>   public class CH06CustomControlWithStateCS3 :     WebControl, IPostBackDataHandler   { // define an event to be raised if the text changes public event EventHandler TextChanged; // private copies of attribute data private String mLabelText = "Label: "; private Color mTextColor = Color.Black; private int mTextboxWidth = 3; private String mText = null; ///********************************************************************** /// <summary> /// This property provides the ability to get/set the text of of the label /// in the control /// </summary> public String labelText { get { return (mLabelText); } set { mLabelText = value; } } // labelText ///*********************************************************************** /// <summary> /// This property provides the ability to set the color of the text /// in the control /// </summary> public Color textColor { get { return (mTextColor); } set { mTextColor = value; } } // textColor ///*********************************************************************** /// <summary> /// This property provides the ability to get/set the width of the text box /// in the control /// </summary> public int textboxWidth { get { return (mTextboxWidth); } set { mTextboxWidth = value; } } // textboxWidth ///*********************************************************************** /// <summary> /// This property provides the ability to set the HTML tag that is used /// as the container for the control /// </summary> protected override HtmlTextWriterTag TagKey { get { return (HtmlTextWriterTag.Div); } } // TagKey ///*********************************************************************** /// <summary> /// This property provides the ability to get/set the text in the text box /// in the control /// </summary> public String text { get { return (mText); } set { mText = value; } } // text ///*********************************************************************** /// <summary> /// This routine renders the HTML output of the control contents /// </summary> /// /// <param name="writer">Set to the HtmlTextWriter to use to output the /// rendered HTML for the control /// </param> protected override void RenderContents(HtmlTextWriter writer) { //output label writer.AddAttribute("color", ColorTranslator.ToHtml(textColor)); writer.RenderBeginTag(HtmlTextWriterTag.Font); writer.Write(labelText); writer.RenderEndTag( ); //output input control writer.AddAttribute(HtmlTextWriterAttribute.Type, "text"); writer.AddAttribute(HtmlTextWriterAttribute.Size, textboxWidth.ToString( )); // output name attribute to identify data on postback writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID); // output value attribute only if value exists if (text != null) { writer.AddAttribute(HtmlTextWriterAttribute.Value, text); } writer.RenderBeginTag(HtmlTextWriterTag.Input); writer.RenderEndTag( ); } // RenderContents ///*********************************************************************** /// <summary> /// This routine processes data posted back from the client /// </summary> /// /// <param name="postDataKey">The key identifier for the control</param> /// <param name="postCollection">The collection of all incoming name /// values</param> /// /// <returns>set true if the server control's state changes as a result /// of the post back; otherwise false /// </returns> public virtual bool LoadPostData(string postDataKey,  NameValueCollection postCollection) { Boolean dataChanged = false; String postbackValue; // check to see if the data changed postbackValue = postCollection[postDataKey]; if (!postbackValue.Equals(text)) { dataChanged = true; } // set the value of the text property from the postback data text = postbackValue; return (dataChanged); } // LoadPostData ///*********************************************************************** /// <summary> /// This routine processes data changed events as a result of the postback /// </summary> public virtual void RaisePostDataChangedEvent( ) { // raise event if a handler is assigned if (TextChanged != null) { TextChanged(this, EventArgs.Empty); } } // RaisePostDataChangedEvent ///*********************************************************************** /// <summary> /// This routine provides the event handler for the control init event. /// It is responsible for registering with the page to indicate this /// control requires the control state6 /// </summary> /// /// <param name="e">Set to the event arguments</param> protected override void OnInit(EventArgs e) { base.OnInit(e); Page.RegisterRequiresControlState(this); } ///*********************************************************************** /// <summary> /// This routine provides the ability to restore the data from the control /// state that was previously saved by the SaveControlState method /// </summary> /// /// <param name="state">Set to the control state to restore</param> protected override void LoadControlState(Object state) { Pair statePair; // check to see if there is any control state to restore if (state != null) { // check to see if the state information contain base data if (state is Pair) { // state contains control state information for the base class so // extract it and pass it along to the base class statePair = (Pair)state; base.LoadControlState(statePair.First); // get the state information for this object text = (String)(statePair.Second); } else { // state contains only simple data so check to see if it is // applicable to this object if (state is String) { // get the state information for this object text = (String)state; } else { // pass state information on to base object base.LoadControlState(state); } } } } // LoadControlState ///*********************************************************************** /// <summary> /// This routine provides the ability to save information in the control /// state /// </summary> /// /// <returns>An object containing the information to store in the control /// state /// </returns> protected override Object SaveControlState( ) { Object baseControlState; Object returnValue = null; baseControlState = base.SaveControlState( ); if (text == null) { returnValue = baseControlState; } else { if (baseControlState == null) { returnValue = text; } else { returnValue = new Pair(baseControlState, text); } } return (returnValue); } // SaveControlState  } // CH06CustomControlWithStateCS3 } 

Example 6-17. Using the custom control using the control state (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master"   AutoEventWireup="false"   CodeFile="CH06DisplayControlWithStateVB3.aspx.vb"   Inherits="ASPNetCookbook.VBExamples.CH06DisplayControlWithStateVB3"   Title="Custom Control Using Control State" %> <%@ Register TagPrefix="ASPCookbook" Namespace="ASPNetCookbook.VBExamples" %> <asp:Content  runat="server" ContentPlaceHolder>   <div align="center" >     Custom Control With State & Events Using Control State (VB)   </div>   <table width="90%" align="center" border="0">     <tr bgcolor="#ffffcc">   <td align="center">     <ASPCookbook:CH06CustomControlWithStateVB3  runat="server" labelText="Enter Age: " textColor="#000080" textboxWidth="5" OnTextChanged="ccAttributes_TextChanged" EnableViewState="false" />   </td> </tr> <tr> <td align="center">   <asp:Label  Runat="server" /> </td> </tr> <tr> <td align="center">   <br/>   <asp:Button  runat="server"  Text="Submit" /> </td> </tr>   </table> </asp:Content> 

Example 6-18. Using the custom control using the control state code-behind (.vb)

 Option Explicit On Option Strict On Namespace ASPNetCookbook.VBExamples   ''' <summary>   ''' This class provides the code beside for   ''' CH06DisplayControlWithStateVB3.aspx   ''' </summary>   Partial Class CH06DisplayControlWithStateVB3     Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _   ByVal e As System.EventArgs) Handles Me.Load    labMessage.Text = "" End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the custom control text ''' changed event. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub ccAttributes_TextChanged(ByVal sender As Object, _    ByVal e As System.EventArgs) labMessage.Text = "Data Changed" End Sub 'ccAttributes_TextChanged   End Class 'CH06DisplayControlWithStateVB3 End Namespace 

Example 6-19. Using the custom control using the control state code-behind (.cs)

 using System; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code beside for /// CH06DisplayControlWithStateCS3.aspx /// </summary> public partial class CH06DisplayControlWithStateCS3 : System.Web.UI.Page { ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) {    labMessage.Text = ""; } // Page_Load ///*********************************************************************** /// <summary> /// This routine provides the event handler for the custom control text /// changed event. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void ccAttributes_TextChanged(Object sender, System.EventArgs e) { labMessage.Text = "Data Changed"; } //ccAttributes_TextChanged } // CH06DisplayControlWithStateCS3 } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

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