Dynamic ViewState in ASP.Net WebForms

If you have worked with ASP.Net MVC 3 you are probably aware of ViewBag property which allows you to pass data from Controller to View by using dynamic properties instead of hard coding strings. While the properties still are not strongly typed and compiler does not check them it can provide an advantage. For example you can avoid casting as it returns dynamic objects. This post will show how to use the same approach in ASP.Net Web Forms for ViewState property giving us a dynamic viewstate class.

In order to access the properties which do not exist at compile time the class has to inherit from DynamicObject. This will allow us to override methods which are called when we get or set dynamic properties. In these methods we will use the ViewState of the current page to store and extract values.

To start with we need let’s create the DynamicViewState class and base page class which will contain the ViewBag property:

public class DynamicViewState : DynamicObject
    {
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            return base.TrySetMember(binder, value);
        }
 
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return base.TryGetMember(binder, out result);
        }
    }

    public class BasePage : Page
    {
        private readonly dynamic viewBag;

        protected dynamic ViewBag
        {
            get { return viewBag; }
        }
    }

The TryGetMember and TrySetMember methods are invokes whenever we get or set properties of ViewBag instance. This means that we should have access to ViewState of the current page so let’s pass the page to the constructor of DynamicViewState:

public class DynamicViewState : DynamicObject
    {
        private readonly BasePage basePage;
 
        public DynamicViewState(BasePage basePage)
        {
            this.basePage = basePage;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            return base.TrySetMember(binder, value);
        }
 
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return base.TryGetMember(binder, out result);
        }
    }

    public class BasePage : Page
    {
        private readonly dynamic viewBag;

        public BasePage()
        {
            viewBag = new DynamicViewState(this);
        }

        protected dynamic ViewBag
        {
            get { return viewBag; }
        }
    }

We now have the page but as ViewState is protected property we cannot access it from our methods. We could add methods for storing and retrieving items from the ViewState but that would allow all other classes to manipulate the page’s ViewState. Instead a better option is to make the DynamicViewState class nested. This way we will be able to access the ViewState property without exposing it to the outside world. The implementation of the DynamicViewState now looks like this:

public class BasePage : Page
    {
        private class DynamicViewState : DynamicObject
        {
            private readonly BasePage basePage;
 
            public DynamicViewState(BasePage basePage)
            {
                this.basePage = basePage;
            }
 
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                basePage.ViewState[binder.Name] = value;
                return true;
            }
 
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                result = basePage.ViewState[binder.Name];
 
                return true;
            }
         }

        private readonly dynamic viewBag;

        public BasePage()
        {
            viewBag = new DynamicViewState(this);
        }

        protected dynamic ViewBag
        {
            get { return viewBag; }
        }
    }

Sample usage looks like this:

public partial class SamplePage : BasePage
   {
       protected void Page_Load(object sender, EventArgs e)
       {
           if (IsPostBack)
           {
               ViewBag.loadCount++;
 
               loadLabel.Text = string.Format("Load count: {0}", ViewState["loadCount"]);
           }
           else
           {
               ViewBag.loadCount = 0;
               loadLabel.Text = "Load count: 0";
           }
       }
   }

As you can see we can mix ViewBag and ViewState together without any issues as ViewBag is simply a façade over ViewState.

If we also override TrySetIndex and TryGetIndex methods we can access ViewBag properties by using indexes exactly in the same way as ViewState. This is how final implementation look like:

public class BasePage : Page
    {
        private class DynamicViewState : DynamicObject
        {
            private readonly BasePage basePage;
 
            public DynamicViewState(BasePage basePage)
            {
                this.basePage = basePage;
            }
 
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                basePage.ViewState[binder.Name] = value;
                return true;
            }
 
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                result = basePage.ViewState[binder.Name];
 
                return true;
            }
 
            public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
            {
                if (indexes.Length == 1)
                {
                    basePage.ViewState[indexes[0].ToString()] = value;
                    return true;
                }
                return base.TrySetIndex(binder, indexes, value);
            }
 
            public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
            {
                if (indexes.Length == 1)
                {
                    result = basePage.ViewState[indexes[0].ToString()];
                    return true;
                }
                return base.TryGetIndex(binder, indexes, out result);
            }
        }
 
        private readonly dynamic viewBag;

        public BasePage()
        {
            viewBag = new DynamicViewState(this);
        }

        protected dynamic ViewBag
        {
            get { return viewBag; }
        }
    }

Apart from dynamic viewstate the above approach can be also used for providing similar implementations for Session, Cache and similar objects.

Full source code with a demo application is available at Dynamic ViewState in ASP.Net WebForms

Avatar
Giorgi Dalakishvili
World-Class Software Engineer