Monday, September 10, 2012

GridView does not have a default Insert Support

Here is a fun fact for you. The ASP.NET GridView control does not have default Insert Support for your objects, even if you are using the "InsertItemTemplate" tag, it doesn't matter! That template is apparently there for other controls that can support it - I am not sure who or which those are. So if you were like me, trying to find a way for the Insert template to just start magically working, don't waste your time. You must create this functionality yourself (OH HAPPY HAPPY JOY JOY!).

It seems like there are only a few controls that support this, like the DetailView control which is excellent for individual records.

Otherwise you will be resigned to using a method like the one described in this article here:
http://weblogs.asp.net/manojkdotnet/archive/2009/10/11/insert-update-and-delete-using-grid-view-and-objectdatasource.aspx

A Few Gotchas to Look Out For
  1. As mentioned above, don't bother with the <InsertItemTemplate /> tag, much like the goggles, they do nothing! If you added them already, don't delete them, just do a find and replace (Ctrl + H) on them and replace it with "FooterTemplate" because you are going to need it anyhow.
  2. As much as it pains me to say, any <asp:BoundFields /> you want to be able to insert will have to be converted to <asp:TemplateFields />, the good news is this is easy to do with the page Designer tool. I suggest using it as it provides a link you can click that does this for you. However once done, it will not provide an option to undo it.
  3. If you are having trouble making those <FooterTemplate /> fields show up, it is because you have to enable the ShowFooter property in the GridView itself.
  4. You will have to include a column that will house a button just for inserting new rows.
  5. You will have to wire up an event to that button and handle the creation manually because your object data source (ODS), if you have one, will not handle it automatically.
  6. After hitting the button, your grid will not refresh itself unless you call your grids DataBind() method.
  7. If your grid is empty, your footer will not show up, therefore you cannot add any new records. I suggest making sure that you check your data source before it reaches your ODS or GridView so that this doesn't happen. If you have zero rows, add 1 bogus row and just make sure the user cannot add or edit it.
Here are more links that helped me figure this all out:
Have fun I know I did...

My Example Html

<asp:GridView ID="gvw" runat="server" 
            EmptyDataText="There are no Objects to display."
            AutoGenerateColumns="False" 
            ShowFooter="True"
            DataSourceID="ods"
            onrowcommand="gvw_RowCommand" >
    <Columns>
        <asp:TemplateField HeaderText="ObjectID" InsertVisible="False" Visible="False">
            <ItemTemplate>
                <asp:Label ID="lblObjectID" runat="server" Text='<%# Bind("ObjectID") %>' />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Amount">
            <ItemTemplate>
                <asp:Label ID="lblAmount" runat="server" Text='<%# Bind("Amount", "{0:n}") %>'></asp:Label>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="txtAmount" runat="server" Text='<%# Bind("Amount", "{0:n}") %>' Width="100px"></asp:TextBox>
            </EditItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="txtAmount" runat="server" Text='<%# Bind("Amount", "{0:n}") %>' Width="100px"></asp:TextBox>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Series">
            <ItemTemplate>
                <asp:Label ID="lblSeries" runat="server" Text='<%# Bind("Series") %>'></asp:Label>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="txtSeries" runat="server" Text='<%# Bind("Series") %>' Width="100px"></asp:TextBox>
            </EditItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="txtSeries" runat="server" Text='<%# Bind("Series") %>' Width="100px"></asp:TextBox>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Notes">
            <ItemTemplate>
                <asp:Label ID="lblNotes" runat="server" Text='<%# Bind("Notes") %>'></asp:Label>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:TextBox ID="txtNotes" runat="server" Text='<%# Bind("Notes") %>' Width="100px"></asp:TextBox>
            </EditItemTemplate>
            <FooterTemplate>
                <asp:TextBox ID="txtNotes" runat="server" Text='<%# Bind("Notes") %>' Width="100px"></asp:TextBox>
            </FooterTemplate>
        </asp:TemplateField>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" ShowHeader="false" 
            InsertVisible="False" HeaderText="" />
        <asp:TemplateField ShowHeader="False" HeaderText="">
            <FooterTemplate>
                <asp:Button ID="btnAdd" 
                            runat="server" 
                            CommandName="Insert" 
                            Text="Add" 
                            OnClick="btnAdd_OnClick" />
            </FooterTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ods" runat="server"
    TypeName="Namespace.ClassName" <!-- Always use the fully qualified class name for your class that has your methods in it --> 
    DataObjectTypeName="NameSpace.ObjectName" 
    SelectMethod="GetObject" <!-- Select Method in Namespace.ClassName -->
    DeleteMethod="DeleteObject" <!-- Delete Method in Namespace.ClassName --> 
    UpdateMethod="UpdateObject" <!-- Update Method in Namespace.ClassName -->
    oninserting="ods_Modification" <!-- I happen to use the same handler for all of these operations -->
    onupdating="ods_Modification"
    OnDeleting="ods_Modification" >
    <SelectParameters> <!-- my select method has a required parameter, it gets its value from the QueryString -->
        <asp:QueryStringParameter DefaultValue="0" Name="ObjectID" 
            QueryStringField="ObjectID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

My Example Code Behind
I am only going to show the event handler for btnAdd_OnClick because everything else is not completely relevant right now (I will be writing a new article about it soon). I will say however that if you are not showing your column that has the ID in it (Visible="false") then your ODS's base object will not have that ID and your update and delete methods will fail.

protected void btnAdd_OnClick(object sender, EventArgs e)
{
 ObjectName obj = new ObjectName();

 //Load your new object with values from the footer
 obj.Amount = Convert.ToDecimal(gvw.FooterRow.GetTextBox("txtAmount").Text);
 obj.Notes = gvw.FooterRow.GetTextBox("txtNotes").Text;
 obj.Series = gvw.FooterRow.GetTextBox("txtSeries").Text;

 //This is the insert method that the ODS could not use
 Namespace.ClassName.InsertObject(obj); 

 //In order for your GridView to refresh itself you must do this step
 //otherwise it will feel like nothing happened when it really did
 gvw.DataBind();
}

No comments:

Post a Comment