Creating a Gridview and Formview Master/Child page

For those of you who don't know what CRUD stands for, it is Create, Read, Update, Delete. These are the four main "modes" we need for data entry and should be the most basic of operations we code for in our applications.

A simple scenario is our site user list. We have a list of users that we need to manage. We need to

  • Create New Users
  • View our list of Users
  • Update existing Users
  • Delete Users when they are no longer active

This is CRUD operation. An easy way to build a CRUD system is to use one Gridview and one Formview to build a "Master/Child" scenario. This scenario lists all users in the Gridview (the "Read" part of CRUD). Gridviews can also be configured to allow Deletes. When you select an item in the Gridview, that item is opened in the Formview to Edit. You can save that, and re-bind the Gridview to see those changes. Similarly, use the Formview to Create new users. The process seems simple enough, so let's jump into some code.

Firstly, let's create a blank web form. Then, we create a data source. This datasource will be the connection between the Gridview and the database that stores our User data.

I generally like to create two datasources. One for the Gridview and one for the Formview. This just simplifies the process a little because two datasources let's us manage our parameters separately. I will show you the code the Formview datasource and you will see what I mean.
EDIT:

<asp:SqlDataSource ID="sqlUsers" runat="server" 
	ConnectionString="<%$ ConnectionStrings:MyConnectionString %>" 
        SelectCommand="proc_User_Get" SelectCommandType="StoredProcedure">
        <SelectParameters>
            <asp:Parameter DefaultValue="0" Name="UserID" Type="Int32" />
            <asp:Parameter DefaultValue="0" Name="IsVisible" Type="Int32" />
        </SelectParameters>
</asp:SqlDataSource>

<asp:SqlDataSource ID="sqlSingleUser" runat="server" 
        ConnectionString="<%$ ConnectionStrings:MyConnectionString %>" 
        InsertCommand="proc_User_Add" InsertCommandType="StoredProcedure" 
        SelectCommand="proc_User_Get" SelectCommandType="StoredProcedure" 
        UpdateCommand="proc_User_Update" UpdateCommandType="StoredProcedure">
        <InsertParameters>
            <asp:Parameter Name="UserID" Type="Int32" Direction="Output" />
            <asp:Parameter Name="UserName" Type="String" />
            <asp:Parameter Name="FirstName" Type="String" />
            <asp:Parameter Name="LastName" Type="String" />
            <asp:Parameter Name="Email" Type="String" />
            <asp:Parameter Name="IsVisible" Type="Boolean" />
        </InsertParameters>
        <SelectParameters>
            <asp:ControlParameter ControlID="gvUser" DefaultValue="SelectedValue" 
                Name="UserID" PropertyName="SelectedValue" Type="Int32" />
            <asp:Parameter DefaultValue="0" Name="IsVisible" Type="Int32" />
        </SelectParameters>
        <UpdateParameters>
            <asp:Parameter Name="UserID" Type="Int32" />
            <asp:Parameter Name="UserName" Type="String" />
            <asp:Parameter Name="FirstName" Type="String" />
            <asp:Parameter Name="LastName" Type="String" />
            <asp:Parameter Name="Email" Type="String" />
            <asp:Parameter Name="IsVisible" Type="Boolean" />
        </UpdateParameters>
</asp:SqlDataSource>

You can see that the parameter list is a lot longer for dsSingleUser because it is handling more operations (Select, Insert and Update). You can also see that there are three different Commands that this datasource does and each of these commands have SQL Stored Procedures attached. This is really powerful because it means we can tell the datasource to execute different stored procedures for different operations.

OK, so we have two datasources. Let's create the Gridview control and bind it to the appropriate datasource.
EDIT:

<asp:GridView ID="gvUser" runat="server" AllowSorting="True" 
        AutoGenerateColumns="False" CssClass="grid" DataKeyNames="UserID" 
        DataSourceID="sqlUsers" onselectedindexchanged="gvUser_SelectedIndexChanged" 
        onsorting="gvUser_Sorting">
        <Columns>
            <asp:CommandField ShowSelectButton="True"></asp:CommandField>
            <asp:BoundField DataField="UserID" HeaderText="ID" InsertVisible="False" 
                ReadOnly="True" SortExpression="UserID"></asp:BoundField>
            <asp:BoundField DataField="UserName" HeaderText="UserName" 
                SortExpression="UserName"></asp:BoundField>
            <asp:BoundField DataField="UserFullName" HeaderText="Full Name" ReadOnly="True" 
                SortExpression="UserFullName"></asp:BoundField>
            <asp:CheckBoxField DataField="IsVisible" HeaderText="Active?" 
                SortExpression="IsVisible"></asp:CheckBoxField>
        </Columns>
</asp:GridView>

Let me step through the attributes in the Gridview control.

  • ID - this is critical to our project. We need to use this ID later when we set up the Formview. Essentially, the Formview will look to the Gridview for information to use when getting information from the database. Modifying the Gridview ID (from the default Gridview1) makes it easier to reference later.
  • OnSelectedIndexChanged - this is some codebehind that we will cover in another article. But what this does is re-bind the Formview to the selected item when a Gridview item is selected.
  • DataKeyNames - this is the field that will be exposed as the selected value when a row is clicked. We will see this later when we bring in the Formview.
  • Columns section - the items in this section form the columns of the grid.
  • asp:CommandField - this kind of field usually offers a button or a link. In this situation, we are creating a link that will initiate the SelectedIndexChanged event on the Gridview.
  • asp:BoundField - this kind of field is a record from the datasource. We bind the field to the database field so that updates, etc are reflected in the grid. You can see from the DataField attribute which database field is being bound.

So, now we can open the page and the Gridview will give us a list of Users. Magic! But, if the database is empty, the list will be empty. So we need to create a data entry form so we can create new Users. This is where the Formview comes in.
EDIT:

<asp:FormView ID="fvUser" runat="server" CssClass="formview" 
        DataSourceID="sqlSingleUser" DefaultMode="Edit" 
        oniteminserted="fvUser_ItemInserted" onitemupdated="fvUser_ItemUpdated" 
        Visible="False">
        <EditItemTemplate>

            <div class="formrow">
                <label>User ID</label>
                <asp:Label ID="UserIDLabel1" runat="server" Text='<%# Bind("UserID") %>' />
            </div>

            <div class="formrow">
                <label>UserName</label>
                    <asp:TextBox ID="UserNameTextBox" runat="server" 
                    Text='<%# Bind("UserName") %>' />
            </div>

            <div class="formrow">
                <label>First Name</label>
                <asp:TextBox ID="FirstNameTextBox" runat="server" 
                Text='<%# Bind("FirstName") %>' />
            </div>

            <div class="formrow">
                <label>Surname</label>
                <asp:TextBox ID="LastNameTextBox" runat="server" 
                Text='<%# Bind("LastName") %>' />
            </div>

            <div class="formrow">
                <label>Email</label>
                <asp:TextBox ID="EmailTextBox" runat="server" Text='<%# Bind("Email") %>' />
            </div>

            <div class="formrow">
                <label>Active?</label>
                <asp:CheckBox ID="IsVisibleCheckBox" runat="server" 
                Checked='<%# Bind("IsVisible") %>' />
            </div>

            <div class="button-wrap">
                <asp:Button ID="UpdateButton" runat="server" CausesValidation="True" 
                    CommandName="Update" Text="Save" />
            </div>
        </EditItemTemplate>
        <InsertItemTemplate>

            <div class="formrow">
                <label>UserName</label>
                    <asp:TextBox ID="UserNameTextBox" runat="server" 
                    Text='<%# Bind("UserName") %>' />
            </div>

            <div class="formrow">
                <label>First Name</label>
                <asp:TextBox ID="FirstNameTextBox" runat="server" 
                Text='<%# Bind("FirstName") %>' />
            </div>

            <div class="formrow">
                <label>Surname</label>
                <asp:TextBox ID="LastNameTextBox" runat="server" 
                Text='<%# Bind("LastName") %>' />
            </div>

            <div class="formrow">
                <label>Email</label>
                <asp:TextBox ID="EmailTextBox" runat="server" Text='<%# Bind("Email") %>' />
            </div>

            <div class="formrow">
                <label>Active?</label>
                <asp:CheckBox ID="IsVisibleCheckBox" runat="server" 
                Checked='<%# Bind("IsVisible") %>' />
            </div>

            <div class="button-wrap">
                <asp:Button ID="InsertButton" runat="server" CausesValidation="True" 
                    CommandName="Insert" Text="Save" />
            </div>
        </InsertItemTemplate>
</asp:FormView>

Some of the attributes in the Formview are.

  • DataSourceID - the same attribute as we set in the Gridview. But the Formview uses the second datasource that we set up. Therefore, the Formview can access the command operations and the Stored Procedures. Which means the Formview has access to three different operations; Select, Update and Insert.
  • DefaultMode - you would have seen in the Formview articles I linked up ealier, Formviews have different modes. We can change these modes to get different templates and these templates can present different fields to the user. For example, a read-only table of data will bind labels to the datasource fields (because the user cannot change this data). However, a form must be presented to the user in order to update or create a new record. Formviews use Templates to allow this customisation and you can see there are sections within the Formview for this.
  • EditItemTemplate - any controls you add to this template section will only be visible when the Formview mode is changed to Edit. We will control the Formview mode via code and will discuss that later
  • InsertItemTemplate - any controls you add to this template section will only be visible when the Formview mode is changed to Insert
  • You might also notice that there is no ItemTemplate section in the Formview. I often leave this section out because the Master/Detail never actually shows that template. That is, when a Gridview is selected, the Formview is displays in Edit mode. When no Gridview item is selected, the Formview is hidden.

We will also create a button in the page. This button will initiate a New User which we will discuss later.

<asp:Button ID="btnNew" runat="server" 
        Text="New User" onclick="btnNew_Click" />

Now we have a Gridview (bound to datasource dsUser) and a Formview (bound to dsSingleUser). This binding means that the controls can get data from the database and display it when we need it to. Now we need to manage the events that fill the data fields in the controls.

When the user comes to this page, we want to show the Gridview and list all users. However, until the user selects a User row, we have no information to show in the Formview (no SelectedValue in the Gridview). That is why the the Visible attribute in the Formview is set to False. Then we have control over when the Formview is made visible to the user.

Here is the codebehind page and I will explain each of the method within the code.
EDIT

protected void Page_Load(object sender, EventArgs e){
}
protected void gvUser_SelectedIndexChanged(object sender, EventArgs e)
{
	//   when an item in the Gridview is selected, run this code
	// change the Formview mode to Edit so we can display the EditItemTemplate
	fvUser.ChangeMode(FormViewMode.Edit);
	// make the Formview visible
	fvUser.Visible = true;
}
protected void btnNew_Click(object sender, EventArgs e)
{
	gvUser.SelectedIndex = -1;
	// change the Formview mode to Insert so we can display the InsertItemTemplate    
	fvUser.ChangeMode(FormViewMode.Insert);
	// make the Formview visible
	fvUser.Visible = true;
}
protected void fvUser_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
	// when the Formview "Update" button is pressed, tell the Gridview to go and get the latest data
	gvUser.DataBind();
}
protected void fvUser_ItemInserted(object sender, FormViewInsertedEventArgs e)
{
	// when the Formview "Insert" button is pressed, tell the Gridview to go and get the latest data
	gvUser.DataBind();
}

I have also included some CSS classes in the code to show you how you can format your page. I haven't included the CSS because the look and feel is completely up to you. But you can see where the CSS attributes can be applied to the code.

There are lot of other things you can do with your Gridviews and Formviews, but this is an indication of the basics. In most situations, this hierarchical approach should do the job for you.

Til next time ...