ListBoxes and DropdownLists are 2 controls that I always disable ViewState for. I will explain why this is essential for page performance and I’ll show you how you can do this and still get the correct selected values. I’ll then go into an example of how you can do this even when the values are encrypted using a random salt.
Why Should I Disable ViewState?
It’s no secret that ViewState can be a beast if there is a lot of state that must be saved. If you have a ListBox with a few hundred entries then it can easily get out of hand because each ListItem in the ListBox or DropDownList is persisted in ViewState. This is done so that we do not have to hit the database again to pull the values out. However, it usually is quicker to actually go back to the database instead of using all of that bandwidth to send the data to the client and then back again.
In general, you should get into the habit of thinking about what the ViewState for a control will look like. There are certain controls where I always turn off ViewState because it really is not worth the overhead a lot of the time. Now that we have that taken care of, let’s get onto how to do it.
How Can I Disable ViewState?
If you understand the Page Lifecycle then this is a pretty simple exercise. As with Dynamic Controls, we need to bind our ListBox or DropDownList before the Control Hierarchy is created. That means that we want to bind it in the Page_Init because it is too late by the time we reach Page_Load.
protected void Page_Init(object sender, EventArgs e)
{
// Need to bind before our control hierarchy is recreated
BaseballListBox.DataSource = Baseball.GetBaseballTeams();
BaseballListBox.DataTextField = "Key";
BaseballListBox.DataValueField = "Value";
BaseballListBox.DataBind();
BaseballDDL.DataSource = Baseball.GetBaseballTeams();
BaseballDDL.DataTextField = "Key";
BaseballDDL.DataValueField = "Value";
BaseballDDL.DataBind();
}
protected void SubmitBtn_Click(object sender, EventArgs e)
{
string selListItems = "";
foreach (ListItem item in BaseballListBox.Items)
{
if (item.Selected)
{
selListItems += string.Format("{0} ({1}), ", item.Value, item.Text);
}
}
if (selListItems.Length > 0)
{
ListBoxSelVal.Text = selListItems.Substring(0, selListItems.Length - 2);
}
DDLVal.Text = string.Format("{0} ({1})", BaseballDDL.SelectedValue, BaseballDDL.SelectedItem.Text);
}
As you can see, we rebind each time in the Page_Init and then when we get to the Submit button click event, we can loop over the ListBox Items collection and check the Selected property to get all of our selected values. For the DropDownList (or for a ListBox where we only allow one item to be selected) we can go straight to the SelectedValue property, just as if ViewState were enabled.
Even if new values have been added to the database for the DropDownList since our last request, we still get the correct values by checking SelectedValue instead of SelectedIndex. The SelectedIndex is volatile and definitely is not the recommended way to go about it (especially when we rebind from the database).
Adding a Twist - Encrypted Values
When we encrypt the values of the ListBox or DropDownList, we tend to use a salt of some sort, which is usually a random number, Date, etc. This introduces a problem, however - the random number will be different each PostBack, which will make the values different and we won’t be able to tell what values from the current list match up to the ones that were selected last time.
One way to get around this problem is to store the random number that you use for a salt in the ViewState. On subsequent requests we can grab the random number from ViewState and use that to re-encrypt our values each request.
private int salt;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
Random rand = new Random();
salt = rand.Next();
ViewState["Rand"] = salt;
BindControls();
}
}
///
/// On all postbacks we want to get our original random number seed
/// and rebind using that. Our encrypted values would be different
/// if we had a new seed and we would lose our selected values
///
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
salt = (int)ViewState["Rand"];
BindControls();
}
private void BindControls()
{
BaseballListBox.DataSource = Baseball.GetBaseballTeamsEncrypted(salt);
BaseballListBox.DataTextField = "Key";
BaseballListBox.DataValueField = "Value";
BaseballListBox.DataBind();
BaseballDDL.DataSource = Baseball.GetBaseballTeamsEncrypted(salt);
BaseballDDL.DataTextField = "Key";
BaseballDDL.DataValueField = "Value";
BaseballDDL.DataBind();
}
This time it is a little bit different because we need information from the ViewState, but we still need to bind before the control hierarchy is completely reconstructed. Therefore, we override LoadViewState, get the salt out and use that to bind our controls. You need to keep in mind that LoadViewState is only called on PostBacks so for the initial binding we can use the Page_Load event to bind the ListBox/DropDownList and store the value in ViewState.
Conclusion
Rebinding ListBoxes and DropDownLists is really an exercise in knowing the Page Lifecycle. If you know when the controls and ViewState are reconstructed then it is fairly simple to disable ViewState on a control and still get the correct values back.
