View state. We usually hate it and love it at the same time. It's definitely a creature of comfort: having our controls automatically maintaining state is definitely a useful thing. But it can also be quite hazardous to your page performance - especially when used with a GridView that has many rows. This big-ass hidden field that gets generated is sure to make your users' round-trips quite a pain. And so the million dollar question is...
How do I disable view state and stay alive?
When you go ahead and mark that GridView of yours with EnableViewState="false" you'll notice that you now have to bind to your DataGrid's DataSource on every post-back. Otherwise, how would it know what rows to render? You have to do it, since the entire page is rendered on each and every post-back. And so you need to get those rows back from the Database even though nothing has changed (sure, you can use caching to eliminate this cost, but caching is not always easy to achieve).
But, if you use an UpdatePanel correctly, you can re-render the grid only when it changes, and still leave the view state off. Let's see how we can achieve this. Here's our page markup and code behind:
<form id="form1" runat="server">
<asp:TextBox ID="FirstName" runat="server"/>
<asp:TextBox ID="LastName" runat="server"/>
<asp:Button ID="AddPerson" runat="server" Text="Add" OnClick="AddPerson_Click" />
<asp:GridView runat="server" ID="PeopleGrid">
</asp:GridView>
</form>
public partial class PeoplePage : System.Web.UI.Page
{
private PeopleRepository _peopleRepository = new PeopleRepository();
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
BindGrid();
}
}
protected void AddPerson_Click(object sender, EventArgs e)
{
_peopleRepository.Add(new Person(FirstName.Text, LastName.Text));
BindGrid();
}
private void BindGrid()
{
PeopleGrid.DataSource = _peopleRepository.GetAll();
PeopleGrid.DataBind();
}
}
All is nice and simple: we have a grid, and two text boxes that allow us to add a person to the grid. Note that we only access the data source once on the very first page-load, and whenever we add a new person. Not on every post-back.
Now, if we wanted to turn view-state off, we'd have to make a few changes. This is how our markup will now look like:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ChildrenAsTriggers="false" UpdateMode="Conditional" runat="server" ID="PeoplePanel">
<ContentTemplate>
<asp:TextBox ID="FirstName" runat="server"/>
<asp:TextBox ID="LastName" runat="server"/>
<asp:Button ID="AddPerson" runat="server" Text="Add" OnClick="AddPerson_Click" />
<asp:GridView runat="server" ID="PeopleGrid" EnableViewState="false">
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</form>
There are a few things to note here. We've added an UpdatePanel, and we changed its UpdateMode to Conditional and its ChildrenAsTriggers property to false. I am doing this in order to manually control exactly when the UpdatePanel gets updated. Since, as you can see, I've turned off the view-state for the grid, I only want it to be updated after the grid was bound - In any other case the grid will be completely empty, since without view-state it can't automatically recover its state.
So when does the UpdatePanel get updated? When we tell it to. Let's change the BindGrid private method.
private void BindGrid()
{
PeopleGrid.DataSource = _peopleRepository.GetAll();
PeopleGrid.DataBind();
PeoplePanel.Update();
}
Note how we manually update the panel whenever we update the grid. The rest of the code is left unchanged. By making sure the update panel is only updated when grid is re-bound, we'll always have rows in our grid, even though view-state is off. This method works beautifully when you have a page with several un-related GridViews. You can turn off view-state for them all, and only update what you need, when you need it.
So, to recap, if you want to turn off view-state for your grid, you need to follow these steps:
- Put an UpdatePanel around the grid, make sure you set UpdateMode to Conditional and its ChildrenAsTriggers property to false.
- Make sure you manually call the Update method of the UpdatePanel every time you bind the grid to the data, and only then.
- Set the EnableViewState property on your grid to false, and enjoy the improved performance!
This way, any post-back that you have on the page that doesn't change the grid will not upload a huge view-state field, and it won't require you hit the data-source again. Also, less data is sent back to the client.
Of course, if you have inline editing in your grid, this is not going to work very well for you, as you'll probably need the state for each row the user edits. The solution for that, is, well... Don't use inline editing. It's not good for you.
If you want to have a look at the entire example code, you can grab it right here.