Building a PreserveProperty Control in ASP.NET 2.0

Building a PreserveProperty Control in ASP.NET 2.0

SP.NET provides a couple of page-level state persistence mechanisms in ViewState and the new ControlState.

While both mechanisms work, they both have some limitations in that they are not deterministic for the application developer?ViewState can be turned off and can be very bulky, and ControlState can be set only from within a control implementation. In this article I’ll show another, more flexible state mechanism using a PreservePropertyControl that allows automatic persistence and restoration of field values automatically without requiring ViewState.

ASP.NET 1.x provides ViewState and ASP.NET 2.0 adds ControlState. Both mechanisms provide the ability to persist page-specific state across a single page postback by embedding the state data into the page as a hidden form variable called __VIEWSTATE. Both work for specific scenarios but I also think that both have serious limitations that make them inappropriate in some scenarios and more difficult to use than they have to be.

Wouldn’t it be nice to have a built-in mechanism to declaratively store a property value of a control or the page and have it automatically restored when the page is posted back?

Sounds a lot like ViewState, right? But ViewState is not declarative and not easily controlled. If you turn off ViewState, as I do whenever I can, you immediately lose the ability to track any values in ViewState at all. Controls stop tracking, and you can no longer use the ViewState collection to store values from your application code. Turn ViewState on and you end up picking up all changed values of all controls that have changed non-postback values and that have ViewState enabled. In other words it’s an all-or-nothing approach, even though you often want to persist only one or two values of a page.

I ran into this a couple of days ago when I was dealing with a complex DataGrid. The DataGrid is set up with ViewState off. There are many actions, like Delete, Update Status, etc., that fire postbacks from each row. There’s also paging and sorting. Without ViewState it gets real messy trying to get the DataGrid to keep track of the CurrentPageIndex properly.

Wouldn’t it be nice if you could do something like this?

   this.gdGrid.PreserveProperty("CurrentPageIndex")
ViewState is an all or nothing mechanism. Once enabled it tracks all changes to controls.

And you’re done? You don’t need to use ViewState. You can choose to have a single value persisted instead of all the DataGrid data. The same would go for persisting properties of any other control like the ForeColor of a button:

   this.btnSubmit.PreserveProperty("ForeColor")

This would work without ViewState enabled and you’d stick this code at the beginning of Page_Load() or other code that runs on any request. ASP.NET would then automatically write out the value at the end of the request and restore it when the page loads again on a postback. Now that’s a page-level state mechanism that’s easy to use and flexible.

Controls could internally use the same mechanism. Controls could persist their state to this same statebag deterministically similar to the way ControlState does in ASP.NET 2.0. ControlState is new in ASP.NET 2.0, and I’ll talk more about it later in this article.

Unfortunately, this approach of using a method on the Control class assumes a change to the Control class by adding a PreserveProperty method, which would then be available on all controls automatically. But making changes to the Control class is an option only if you are Microsoft, as you can’t extend Control and have other stock controls inherit from a modified Control class.

The next best solution is to build an external control that you can drop onto a page or add to a control to provide this same functionality. In this article, I’ll show you how to build such an extender control.

Introducing the PreservePropertyControl
The PreservePropertyControl is an ASP.NET 2.0 custom server control. The downloadable code for this article also contains an ASP.NET 1.1 version of the source courtesy of Peter Bromberg who ported it back to 1.1. I used ASP.NET 2.0 mainly to take advantage of generics for the persisted properties rather than implementing a custom collection. .NET 2.0 makes it a cinch to create strongly-typed collections with generics. You can then use the designer to edit these collections using the default Collection editor. Another 2.0 feature, ControlState, provides the default storage mechanism for the PreserveProperty control. ControlState makes it easy to persist an internal structure without having to worry about encoding the structure for embedding into the page.

I implemented the control as a server control that can be defined on the page declaratively and has designer support. For example you can declare the control like this:

                              

You can do this with script markup inside of your ASP.NET page, or use the designer and the default collection editor to enter the preserved properties with Visual Studio. To persist a value, you specify the control’s UniqueID and the property or field name to persist. You can persist control values as well as values on the Page object as shown with the __Page ControlId above.

Of course, you can also use code to accomplish the same thing.

   protected PreservePropertyControl Persister=null;   protected void Page_Load(object sender, EventArgs e)   {      this.Persister=new PreservePropertyControl();      this.Persister.ID = "Persister";      this.Controls.Add(Persister);      this.Persister.PreserveProperty(         this.btnSubmit, "ForeColor");      this.Persister.PreserveProperty(         this, "CustomerPk");   }

When using code, it’s more efficient to pass an actual control reference rather than a string ID?the control caches the control reference and uses it later in the page cycle to write the values into its storage container.

ControlState is accessible only inside of a control and not on the Page level.

Note that you get a lot of flexibility with this mechanism, and you can basically store anything in this state container as long as you can reference it through a control instance. Because the Page object is also a control you can attach a Protected property for anything you want persisted. It’s quite convenient, for example, to add a Pk reference to a page so you can keep track of your current edit context without requiring ViewState or a hidden variable.

You can even store entire objects. Like ViewState, you can store any serializable object in the PreservePropertyControl. The nice thing about this approach is you set up the PreserveProperty() call once and after that you can just reference the property. So if you persist the CustomerPk as shown above you can simply reference the CustomerPk property on the page and always get the correct value back without having to explicitly access a state mechanism. You can just say this.CustomerPk and the value will be up to date!

This approach is even easier than ViewState because it eliminates the need to talk to the state container at all. You don’t need to access a statebag, double-check for null values, etc. The control manages that for you.

Control developers can also use the control internally to map their own properties by using a private copy of the control.

   public class CustomControl : Control   {      PreservePropertyControl Persister = null;      protected string value = null;      protected override void OnLoad(EventArgs e)      {         this.Persister =             new PreservePropertyControl();         this.Persister.ID = "__" + this.ID;         this.Persister.StorageMode =             PropertyStorageModes.HiddenVariable;         this.Persister.PreserveProperty(            this,"value");         this.Controls.Add(this.Persister);         base.OnLoad(e);      }   } 

Once this is in place the control developer doesn’t have to keep persisting values into any special store like ViewState[“value”] or a custom container, but can simply reference the property as usual, all without ViewState being active.

You can check out the control and a small sample page that demonstrates the functionality by downloading the source code provided with this article. The .zip file contains both the 2.0 and 1.1 versions.

How the PreserveProperty Control Works
The implementation of the control is pretty simple. It has a PreservedProperties collection that holds the ControlID (or instance if available) and the property name. When you call the PreserveProperty() method on the control or declaratively define the control on a page, each ControlId and Property name is stored in the collection. The collection is a Generic List. I love generics for this stuff?no more creating custom collections for child controls!

To get the collection to work as a designable collection you have to use a few custom attributes on the Control class as well as the collection property. You also need to implement AddParsedSubObject (or use the DefaultProperty attribute if you only have a single collection) to get the child control values to be added as PreservedProperty objects from the declarative script definition. Listing 1 shows the class and collection definition.

The code deals with storing the PreservedProperties in a simple strongly-typed List collection. The control uses a custom class to hold the control’s ID, an instance reference if available, and the ID for the control. The PreservedProperties collection is a temporary holding container for the property identification; the values are not actually stored in it until later in the request cycle.

Encoding and Decoding the Persisted Data
The actual hooks for encoding and decoding depend on the StorageMode which includes ControlState, HiddenVariable, SessionVariable, and CachePerPage. All but ControlState require explicit hooks to fire for the encoding and decoding to occur. In these modes, hook calls are made in OnInit() and OnPreRender() of the control. OnInit() fires very early in the page cycle so that the preserved value can be retrieved prior to any other state retrieval. Specifically, the property value is set before ViewState assigns its value or before a postback value is assigned. This makes preserved properties have the lowest priority in value assignment, which is the intended behavior.

The PreserveProperty control lets you work with properties and fields without having to worry about the persistence mechanism.

When the value is stored it’s done in the As part of the PreservePropertyControl.OnPreRender().

ControlState Persistance in ASP.NET 2.0
ControlState is a new feature of ASP.NET 2.0. It’s meant as a control internal state implementation used to store essential data between page postbacks. It uses ViewState for storage, but unlike stock ViewState it is not affected by the setting of EnableViewState and always writes out its values.

You can easily work with ControlState by implementing two methods on the Page class: SaveControlState and LoadControlState. These methods work by returning and retrieving an object value that ASP.NET persists internally. These objects must either be simple types, serializable, or implement a TypeConverter. As with ViewState, the rules are determined through the .NET LosFormatter class, which is an optimized serializer ASP.NET uses for encoding ViewState strings. You can find ControlState in the System.Web.UI namespace.

To enable ControlState you have to tell the page to use it, for example:

   this.Page.RegisterRequiresControlState(this);

You’d typically call this in the OnInit() method of a control.

You then override SaveControlState() to return an object that contains the state. If you have more than one value to persist, a typical object is a Hashtable with all the persisted values in it. When the page posts back, LoadControlState is called with an object parameter that essentially restores that object saved in SaveControlState. It’s a very easy mechanism that deals with all the encoding details. All you do is return an object and off you go. Listing 2 shows the SaveControlState() and LoadControlState() methods for managing ControlState.

SaveControlState() walks through its PreservedProperties collection and starts by finding each control instance. If you used code to add properties, then you likely passed in a control instance reference, but when using declarative script a string reference is stored, and you’ll need to use FindControl to actually locate the control. Once a control reference is available the value for the property is retrieved via Reflection. Note the flags to retrieve both fields and properties and both public and non-public members. Also note that you cannot preserve private members on a Page object.

The next step is to add the value to a Hashtable with the key being a concatenation of the unique ControlID and the property name. Hashtables are efficient at storing key value pairs and persist in a lightweight fashion. The SaveControlState method then returns this Hashtable for persistence.

Loading the data on a postback works this process in reverse. LoadControlState() receives the Hashtable as an input and then runs through the collection, retrieving each key and value. The code in LoadControlState() splits the key back into its ControlID and property name components and FindControl is then used to get an instance, followed by Reflection to set the control’s value.

This is a very straightforward process and using ControlState takes very little code to implement this solution.

Other Persistence Mechanisms
ControlState works fine and is probably the most reliable way to store the persisted data, but if you’re using ASP.NET 1.1 or if you don’t like ViewState persistence (even if permanent) you might want to use a different storage mechanism. The PreservePropertyControl supports four persistence modes:

  • ControlState
  • HiddenVariable
  • SessionVariable
  • CachePerPage

All but ControlState require custom hooks into the page pipeline and a little extra work to serialize the data. When I created the original control for this I used my own serialization mechanism which was less efficient and required quite a bit more code. After a few suggestions from my blog someone suggested that I use the LosFormatter. The LosFormatter is a little known string serializer in the System.Web.UI namespace that you can use to serialize objects. The format used by this serializer is lighter-weight than the full binary formatter because it encodes common types more efficiently, often bypassing the expensive full .NET serialization mechanism. Listing 3 shows the relevant code from the control that demonstrates the non-ControlState storage mechanisms.

For all but the ControlState persistence, both OnInit() and OnPreRender() methods are hooked. OnInit() calls LoadStateFromLosStorage() and OnPreRender() calls SaveStateToLosStorage(). These methods delegate the actual Hashtable generation and parsing to SaveControlState() and LoadControlState() shown above. SaveStateToLosStorage() then calls the LosFormatter to create a string representation of this Hashtable. Each of the different storage mechanisms then stores this string in the appropriate storage mechanism.

With HiddenFormVariable the data is stored as follows:

   this.Page.ClientScript.RegisterHiddenField(      "__" + this.UniqueID, Serialized);

Then, you read it back using code such as:

   RawBuffer = HttpContext.Current.Request.Form[      "__" + this.UniqueID];

When the raw buffer is retrieved it’s deserialized back into a Hashtable and then passed to LoadControlState(), which reassigns the values to the controls.

Session variable storage is a little different and requires a little explanation. Session storage stores the preserved property state in a Session variable that is reused for all pages. This means every page gets the same Session variable instance and there’s one variable per user.

Using Session can be a great boon because if you store the persisted data in the Session object, you’re not shipping it over the wire, which means smaller pages. Session storage also tends to be much faster than ViewState encoding of any kind if using InProc storage because no serialization occurs.

You’ll want to use caution with this approach! If you open two windows in the same browser session or use Frames pages that have the same Session active simultaneously, you’ll run into problems with getting the wrong state restored. In the included demo page you can test this by running the page, setting a color, and submitting then pressing Ctrl-N (or Ctrl-T if you have tabs) to create a new browser window in the same browser session. Open the same page, select a different color, and click Show. Now go back to the first page and click the postback button?you should see the color from the second instance in the first instance, which is clearly incorrect. The first instance picked up the persisted data of the second.

This might not be a problem in some internal applications where there’s no reason to run multiple browser windows or frames, but even so, be very careful with this option. You could solve this problem by generating a unique per-page ID for a Session variable, but this would then potentially clutter up Session state with a lot of persisted memory. Session variable storage should be considered only if you work on pages that have a large amount of persisted data and want to avoid sending that data over the wire for each postback.

Similarly, you can use the Cache object. Unlike the Session approach, the Cache approach writes to the session, assigning a new GUID for each page. So every new page for every user creates a new cache entry.

As with Session, be aware of the trade-offs: Using the Cache object you will generate a large number of cache entries, so on a really busy site with many pages, this approach is probably not realistic. Also note that the Cache approach will not work in Web farm environments as Cache doesn’t persist across machines.

Both Cache and Session offer lighter-weight pages and better performance for state storage though, so you might want to experiment with the options.

Both ControlState and HiddenFormVariable approaches are stored in the page itself as POST data so they don’t suffer from these issues. Out of the two, ControlState is the more reliable solution simply because ControlState has its own events that fire as part of the page pipeline and don’t require hooking existing events where timing issues with other controls can potentially occur. I provided a HiddenFormVariable implementation primarily to support ASP.NET 1.x.

Preserve the World
In recent applications I’ve found a lot of uses for this control where I had previously not even considered state storage. When you can control exactly what gets persisted it’s easy to keep persisted state size down and still get a flexible page that provides true property access to the persisted values. I tend to run my pages with ViewState off altogether, so having a control that can persist a few items declaratively can be very helpful. All in all, this control makes life easier in many ways.

The most common scenarios for me are managing List state settings such as SelectedValue properties from list controls, paging, and sorting settings in grids and lists. Storing page-level state like a current record ID also works very well. The beauty is that it becomes much more natural to interact with any preserved properties because you simply reference the properties without any concern for the persistence mechanism?the process is totally transparent once you’ve added it to the PreservePropertyControl. The control basically lets you turn ASP.NET’s promiscuous ViewState policy upside down: Instead of storing everything in ViewState automatically you store only what you explicitly need to store. This can drastically reduce the size required by ViewState in your pages. Check it out and see if you can’t trim your page size considerably using this control.

I hope you’ll find this control useful and that this article has given you a few insights into the various page-level state persistence mechanisms. Go ahead-do your thing for the environment and preserve? in real life and now in your Web code!

If you have any comments, questions or suggestions regarding this article, you can post them at www.west-wind.com/wwthreads/?default.asp?forum=White+Papers.

devx-admin

devx-admin

Share the Post:
Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations.

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI,

Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency tokens have seen a substantial

GlobalFoundries Titan

GlobalFoundries: Semiconductor Industry Titan

GlobalFoundries, a company that might not be a household name but has managed to make enormous strides in its relatively short 14-year history. As the third-largest semiconductor foundry in the

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security in the sector remains strong.

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations. However, the United States’ stance

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are now contemplating constructing domestic manufacturing

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI, ChatGPT is known for its

Future of Cybersecurity

Cybersecurity Battles: Lapsus$ Era Unfolds

In 2023, the cybersecurity field faces significant challenges due to the continuous transformation of threats and the increasing abilities of hackers. A prime example of this is the group of

Apple's AI Future

Inside Apple’s AI Expansion Plans

Rather than following the widespread pattern of job cuts in the tech sector, Apple’s CEO Tim Cook disclosed plans to increase the company’s UK workforce. The main area of focus

AI Finance

AI Stocks to Watch

As investor interest in artificial intelligence (AI) grows, many companies are highlighting their AI product plans. However, discovering AI stocks that already generate revenue from generative AI, such as OpenAI,

Web App Security

Web Application Supply Chain Security

Today’s web applications depend on a wide array of third-party components and open-source tools to function effectively. This reliance on external resources poses significant security risks, as malicious actors can

Thrilling Battle

Thrilling Battle: Germany Versus Huawei

The German interior ministry has put forward suggestions that would oblige telecommunications operators to decrease their reliance on equipment manufactured by Chinese firms Huawei and ZTE. This development comes after

iPhone 15 Unveiling

The iPhone 15’s Secrets and Surprises

As we dive into the most frequently asked questions and intriguing features, let us reiterate that the iPhone 15 brings substantial advancements in technology and design compared to its predecessors.

Chip Overcoming

iPhone 15 Pro Max: Overcoming Chip Setbacks

Apple recently faced a significant challenge in the development of a key component for its latest iPhone series, the iPhone 15 Pro Max, which was unveiled just a week ago.

Performance Camera

iPhone 15: Performance, Camera, Battery

Apple’s highly anticipated iPhone 15 has finally hit the market, sending ripples of excitement across the tech industry. For those considering upgrading to this new model, three essential features come

Battery Breakthrough

Electric Vehicle Battery Breakthrough

The prices of lithium-ion batteries have seen a considerable reduction, with the cost per kilowatt-hour dipping under $100 for the first occasion in two years, as reported by energy analytics

Economy Act Soars

Virginia’s Clean Economy Act Soars Ahead

Virginia has made significant strides towards achieving its short-term carbon-free objectives as outlined in the Clean Economy Act of 2020. Currently, about 44,000 megawatts (MW) of wind, solar, and energy

Renewable Storage Innovation

Innovative Energy Storage Solutions

The Department of Energy recently revealed a significant investment of $325 million in advanced battery technologies to store excess renewable energy produced by solar and wind sources. This funding will

Renesas Tech Revolution

Revolutionizing India’s Tech Sector with Renesas

Tushar Sharma, a semiconductor engineer at Renesas Electronics, met with Indian Prime Minister Narendra Modi to discuss the company’s support for India’s “Make in India” initiative. This initiative focuses on

Development Project

Thrilling East Windsor Mixed-Use Development

Real estate developer James Cormier, in collaboration with a partnership, has purchased 137 acres of land in Connecticut for $1.15 million with the intention of constructing residential and commercial buildings.

USA Companies

Top Software Development Companies in USA

Navigating the tech landscape to find the right partner is crucial yet challenging. This article offers a comparative glimpse into the top software development companies in the USA. Through a

Software Development

Top Software Development Companies

Looking for the best in software development? Our list of Top Software Development Companies is your gateway to finding the right tech partner. Dive in and explore the leaders in

India Web Development

Top Web Development Companies in India

In the digital race, the right web development partner is your winning edge. Dive into our curated list of top web development companies in India, and kickstart your journey to

USA Web Development

Top Web Development Companies in USA

Looking for the best web development companies in the USA? We’ve got you covered! Check out our top 10 picks to find the right partner for your online project. Your

Clean Energy Adoption

Inside Michigan’s Clean Energy Revolution

Democratic state legislators in Michigan continue to discuss and debate clean energy legislation in the hopes of establishing a comprehensive clean energy strategy for the state. A Senate committee meeting

Chips Act Revolution

European Chips Act: What is it?

In response to the intensifying worldwide technology competition, Europe has unveiled the long-awaited European Chips Act. This daring legislative proposal aims to fortify Europe’s semiconductor supply chain and enhance its