New to Telerik UI for ASP.NET AJAX? Download free 30-day trial

Saving the viewstate of RadControls server-side

How to

Save the viewstate of RadControls server-side

Solution

We use a common approach to store our RadControls ViewState and you shouldn't have any problems storing it on the server (when the EnableViewState property for the page in which the control(s) reside is set to True). Our products can also work with custom ViewStateManager classes - just keep in mind that the .aspx web form in which the control(s) is(are) placed should extend from the BasePage class, e.g.

BasePage.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using System.Text;
using System.IO;


namespace ServerSideViewstate
{
    public class BasePage : System.Web.UI.Page
    {
        //Setup the name of the hidden field on the client for storing the viewstate key
        public const string VIEW_STATE_FIELD_NAME = "__VIEWSTATE";

        //Setup a formatter for the viewstate information
        private LosFormatter _formatter = null;     

        public BasePage()
        {
        }

        //overriding method of Page class
        protected override object LoadPageStateFromPersistenceMedium()
        {
            //If server side enabled use it, otherwise use original base class implementation
            if (true == viewStateSvrMgr.GetViewStateSvrMgr().ServerSideEnabled)
            {
                return LoadViewState();
            }
            else
            {
                return base.LoadPageStateFromPersistenceMedium();
            }
        }

        //overriding method of Page class
        protected override void SavePageStateToPersistenceMedium(object viewState)
        {
            if (true == viewStateSvrMgr.GetViewStateSvrMgr().ServerSideEnabled)
            {
                SaveViewState(viewState);
            }
            else
            {
                base.SavePageStateToPersistenceMedium(viewState);
            }
        }

        //implementation of method
        private object LoadViewState()
        {
            if (_formatter == null)
            {
                _formatter = new LosFormatter();
            }

            //Check if the client has form field that stores request key
            if (null == Request.Form[VIEW_STATE_FIELD_NAME])
            {
                //Did not see form field for viewstate, return null to try to continue (could log event...)
                return null;
            }

            //Make sure it can be converted to request number (in case of corruption)
            long lRequestNumber = 0;
            try
            {
                lRequestNumber = Convert.ToInt64(Request.Form[VIEW_STATE_FIELD_NAME]);
            }
            catch
            {
                //Could not covert to request number, return null (could log event...)
                return null;
            }

            //Get the viewstate for this page
            string _viewState = viewStateSvrMgr.GetViewStateSvrMgr().GetViewState(lRequestNumber);

            //If find the viewstate on the server, convert it so ASP.Net can use it
            if(_viewState == null)
                return null;
            else
                return _formatter.Deserialize(_viewState);
        }


        //implementation of method
        private void SaveViewState(object viewState)
        {
            if(_formatter == null)
            {
                _formatter = new LosFormatter();
            }

            //Save the viewstate information
            StringBuilder _viewState = new StringBuilder();
            StringWriter _writer = new StringWriter(_viewState);
            _formatter.Serialize(_writer,viewState);
            long lRequestNumber = viewStateSvrMgr.GetViewStateSvrMgr().SaveViewState(_viewState.ToString());

            //Need to register the viewstate hidden field (must be present or postback things don't 
            // work, we use in our case to store the request number)
            RegisterHiddenField(VIEW_STATE_FIELD_NAME, lRequestNumber.ToString());
        }
    }
}

viewStateSvrMgr.cs

using System;
using System.Collections;
using System.Configuration;

namespace ServerSideViewstate
{
    public class viewStateSvrMgr
    {
        // This uses an array and mod to cycle repeatedly through an array (so limited size)

        //WARNING:  When the user uses the "back" button on the browser, IE will not rerequest the page, so 
        // if the user posts again they need the viewstate to still be present on the server.  Need to set the VIEW_STATE_NUM_PAGES
        // to a tradeoff of number of back pages allowed and 
        // the amount of memory consumed by the viewstate kept per page.  
        private const short VIEW_STATE_NUM_PAGES = 5;       //Number of pages to keep viewstate for

        //Name of storage location for veiwstate information
        private const string SESSION_VIEW_STATE_MGR = "VIEW_STATE_MGR";

        private long lPageCount = 0;    //Number of pages seen by this customer 
        private string[] ViewStates = new string[VIEW_STATE_NUM_PAGES]; //Store for viewstates

        //Determine if server side is enabled or not from web.config file
        public bool ServerSideEnabled
        {
            get
            {
                //Not a problem if someone changes the value in web.config, because new AppDomain will
                // be started and all in process session is lost anyway
                return Convert.ToBoolean(ConfigurationSettings.AppSettings["ServerSideViewState"]);
            }
        }

        public viewStateSvrMgr()
        {
        }

        public long SaveViewState(string szViewState)
        {
            //Increment the total page seen counter
            lPageCount++;

            //Now use the modulas operator (%) to find remainder of that and size of viewstate storage, this creates a
            // circular array where it continually cycles through the array index range (effectively keeps
            // the last requests to match size of storage)
            short siIndex = (short)(lPageCount % VIEW_STATE_NUM_PAGES);

            //Now save the viewstate for this page to the current position.  
            ViewStates[siIndex] = szViewState;

            return lPageCount;
        }


        public string GetViewState(long lRequestNumber)
        {
            //Could cycle though the array and make sure that the given request number is actually
            // present (in case the array is not big enough).  Much faster to just take the
            // given request number and recalculate where it should be stored
            short siIndex = (short)(lRequestNumber % VIEW_STATE_NUM_PAGES);

            return ViewStates[siIndex];
        }


        public static viewStateSvrMgr GetViewStateSvrMgr()
        {
            viewStateSvrMgr oViewStateMgr;

            //Check if already created the order object in session
            if (null == System.Web.HttpContext.Current.Session[SESSION_VIEW_STATE_MGR])
            {
                //Not already in session, create a new one and put in session
                oViewStateMgr = new viewStateSvrMgr();
                System.Web.HttpContext.Current.Session[SESSION_VIEW_STATE_MGR] = oViewStateMgr;
            }
            else
            {
                //Return the session order
                oViewStateMgr = (viewStateSvrMgr)System.Web.HttpContext.Current.Session[SESSION_VIEW_STATE_MGR];
            }

            return oViewStateMgr;
        }
    }
}

Default.aspx

<telerik:RadToolBar ID="RadToolBar1" Runat="server" 
    onbuttonclick="RadToolBar1_ButtonClick">
<CollapseAnimation Type="OutQuint" Duration="200"></CollapseAnimation>
    <Items>
        <telerik:RadToolBarButton runat="server" CheckOnClick="true" Text="Button 0">
        </telerik:RadToolBarButton>
        <telerik:RadToolBarButton runat="server" CheckOnClick="true" Text="Button 1">
        </telerik:RadToolBarButton>
        <telerik:RadToolBarButton runat="server" CheckOnClick="true" Text="Button 2">
        </telerik:RadToolBarButton>
    </Items>
</telerik:RadToolBar>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
protected void RadToolBar1_ButtonClick(object sender, Telerik.Web.UI.RadToolBarEventArgs e)
{
    Label1.Text = e.Item.Text;
}

And you should place the following lines in your web-application Web.config file:

<appSettings> 
    <add key="ServerSideViewState" value="true" /> 
</appSettings> 

For additional information concerning the custom viewstate manager usage please review the following online article:

http://aspalliance.com/articleViewer.aspx?aId=72&vId=1&pId

An alternative approach is discussed in this article on the CodeProject site:

http://www.codeproject.com/KB/aspnet/ViewStatePersistance.aspx

In this article