Skip to main content

FormView Oddities and Using EditItemTemplate to Insert

Every now and then I come across those weird, annoying issues with ASP.NET which very few people seem to have had, and no one seems to have an answer.  It's especially frustrating because I can spend hours (I've spent 4 hours today tracking this one down) trying to figure out what's going on.  Maybe one of the gifts of being a programmer comes the ability to spend 4 hours researching, testing, and focusing on one problem.

In any case, we use FormViews pretty regularly in our ASP.NET applications.  For those who don't know, a FormView is very similar to the DataGrid and GridView controls.  You can place your form fields in either an ItemTemplate, EditItemTemplate or IntertItemTemplate.  This lets you bind the FormView to a DataSource and simple use Bind() to populate the values and update them back into the database.  We've been able to create pages with no, or very, very little code in the code behind using this method, and after a bit of a learning curve I definitely think this is the way to go for building sites.

Our only complaint is that you essentially duplicate form fields for each template.  For example, you may want to make the user name field read-only in the EditItemTemplate, but editable in the InsertItemTmplate. 

We (well, my wife anyway. . . Why are women always smarter than men?) came up with a solution where you can use the EditItemTemplate for both updates and inserts.  Essentially the SelectCommand will always return a row, even if the primary key value passed in is null/0.

So, we'll have a FormView for adding and editing builders which looks like this:


With the aspx code having:

   1:      <asp:FormView ID="fvContactInfo" runat="server" DataSourceID="odsContact" DataKeyNames="BusinessID,UserName"

   2:          DefaultMode="Edit" Width="100%">

   3:          <EditItemTemplate>

   4:              <%-- Business Name --%>

   5:              <div class="inputRow">

   6:                  <asp:Label ID="lblBusinessName" runat="server" Text="Business Name" CssClass="inputLabel"

   7:                      AssociatedControlID="txbBusinessName" />

   8:              </div>

   9:              <div class="inputRow">

  10:                  <asp:TextBox ID="txbBusinessName" runat="server" MaxLength="150" Text='<%#Bind("BusinessName") %>'

  11:                      CssClass="inputBox oneColumn" />

  12:                  <asp:RequiredFieldValidator ID="rqvBusinessName" ValidationGroup="CreateUserWizard1"

  13:                      runat="server" ControlToValidate="txbBusinessName" ErrorMessage="Business Name is required."

  14:                      Text="*" ToolTip="Business Name is required." />

  15:              </div>

  16:              <%-- Buttons --%>

  17:              <div class="inputRow" id="rowButtons" runat="server">

  18:                  <asp:Button ID="btnSave" runat="server" Text="Save" CommandName="Update" />

  19:                  <asp:Button ID="btnBack" runat="server" Visible="false" Text="Go back" />

  20:              </div>

  21:          </EditItemTemplate>

  22:      </asp:FormView>


  24:  <%-- Data Source --%>

  25:  <asp:ObjectDataSource ID="odsContact" runat="server" SelectMethod="GetData" TypeName="BCDatasetTableAdapters.BusinessTableAdapter"

  26:      UpdateMethod="Update">

  27:      <SelectParameters>

  28:          <asp:Parameter Name="UserName" Type="String" />

  29:      </SelectParameters>

  30:      <UpdateParameters>

  31:          <asp:Parameter Name="BusinessName" Type="String" />

  32:          <asp:Parameter Name="UserName" Type="String" />

  33:          <asp:Parameter Name="BusinessID" Type="Int32" />

  34:      </UpdateParameters>

  35:  </asp:ObjectDataSource>

And a simple SQL procedure:

   1:  CREATE PROCEDURE [dbo].[sp_bc_BusinessSelect]

   2:  (

   3:      @UserName nvarchar(256)

   4:  )

   5:  AS

   6:      SET NOCOUNT ON;

   7:  BEGIN

   8:      IF @UserName is null 

   9:          BEGIN    

  10:              SELECT 

  11:                  TOP 1

  12:                  -1 BusinessID, 

  13:                  null BusinessName, 

  14:                  '' UserName

  15:              FROM

  16:                  bc_Business

  17:          END

  18:      ELSE

  19:          BEGIN

  20:              SELECT     

  21:                  TOP 1 

  22:                  IsNull(bc_Business.BusinessID, -1) BusinessID, 

  23:                  bc_Business.BusinessName

  24:                  bc_Business.UserName

  25:              FROM         

  26:                  bc_Business 

  27:              WHERE 

  28:                  UserName = @UserName

  29:              ORDER BY 

  30:                  BusinessName

  31:          END

  32:  END

While this is pretty straightforward, there is one problem which came up today.  When there are no builders at all in the builder table, the stored procedure will not return any rows (including the blank row your FormView expects).

That's really due to the fact that in lines 15 and 16 I specify a FROM table, though it's always possible that table is empty.  A better solution is to rewrite the query to look like this:

IF @UserName is null 



                TOP 1

                -1 BusinessID, 

                null BusinessName, 

                '' UserName



What's actually interesting is how the FormView handles the child controls in the Edit template when no data is returned.  When you attempt to run the following code:

   1:  Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load


   3:      odsContact.SelectParameters("UserName").DefaultValue = UserName

   4:      CType(fvContactInfo.FindControl("lblBusinessName"), Label).Text = "Enter new business name here:"


   6:  End Sub

You would expect line 4 to return either a reference to the label or, at worst, Nothing for the control, even if you place it in the FormViews DataBound or Unload event.

You actually receive an error that the FormView itself fvContactInfo threw a NullReferenceException.  Whenever you are binding a FormView to have the Edit mode handle both inserts and updates.

Accounting for the return of a blank row though, this is a great method to reduce the need to duplicate form fields in all templates.  The same template above which used Edit, Item and Insert templates would look similar to the following:

   1:  <asp:FormView ID="fvContactInfo" runat="server" DataSourceID="odsContact" DataKeyNames="BusinessID"

   2:      DefaultMode="Edit" Width="100%">

   3:      <EditItemTemplate>

   4:          Business Name:

   5:          <asp:TextBox ID="BusinessNameTextBox" runat="server" Text='<%# Bind("BusinessName") %>'>

   6:          </asp:TextBox><br />

   7:          User Name:

   8:          <asp:Label ID="UserNameLabel" runat="server" Text='<%# Bind("UserName") %>'>

   9:          </asp:Label><br />

  10:          <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"

  11:              Text="Update">

  12:          </asp:LinkButton>

  13:          <asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False" CommandName="Cancel"

  14:              Text="Cancel">

  15:          </asp:LinkButton>

  16:      </EditItemTemplate>

  17:      <InsertItemTemplate>

  18:          Business Name:

  19:          <asp:TextBox ID="BusinessNameTextBox" runat="server" Text='<%# Bind("BusinessName") %>'>

  20:          </asp:TextBox><br />

  21:          User Name:

  22:          <asp:TextBox ID="UserNameTextBox" runat="server" Text='<%# Bind("User Name") %>'>

  23:          </asp:TextBox><br />

  24:          <asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert"

  25:              Text="Insert">

  26:          </asp:LinkButton>

  27:          <asp:LinkButton ID="InsertCancelButton" runat="server" CausesValidation="False" CommandName="Cancel"

  28:              Text="Cancel">

  29:          </asp:LinkButton>

  30:      </InsertItemTemplate>

  31:      <ItemTemplate>

  32:          BusinessName:

  33:          <asp:Label ID="BusinessNameLabel" runat="server" Text='<%# Bind("BusinessName") %>'>

  34:          </asp:Label><br />

  35:          UserName:

  36:          <asp:Label ID="UserNameLabel" runat="server" Text='<%# Bind("UserName") %>'></asp:Label><br />

  37:          <asp:LinkButton ID="EditButton" runat="server" CausesValidation="False" CommandName="Edit"

  38:              Text="Edit">

  39:          </asp:LinkButton>

  40:      </ItemTemplate>

  41:  </asp:FormView>

Which quickly becomes a nightmare to update.  Have fun!




ChrisS said…
Good example, but u need a code formatter. Check out CopySourceAsHTML. That is the bomb for lifting your junk from VS.

Keep it fresh, I'm a reader!

Popular posts from this blog

Advantages and Disadvantages of Using Microsoft Access

I've answered this question in some form or another far more times than I care to count.  Most often it's a question of "why do I need a fancy Web application when I can just build this myself in two days in Access.  I mean, the data's already in Excel."  So I figured I'd post out what I threw together, I know I've missed some points. Overview Microsoft Access is an ideal solution for relatively small datasets and a limited number of users. From the Microsoft Web site: “As a desktop database, Access is well suited for small, departmental applications. These applications may start as one user’s project. For example, an employee realizes that productivity can be increased if a paper-based process is automated with an Access application. Other users in the department recognize that they can take advantage of the application if additional features are added. As more features are added, more employees run the application. As time goes by, more and more Access

Beryllium Spheres

I'm sitting here at home watching The Shadow , easily one of the best movies made based on one of the best old time radio shows.  I hadn't picked up on this earlier, but the weapon used to destroy the city is none other than the same power source used to power the NSEA Protector in Galaxy Quest . I never knew Beryllium was so cool.  Now I want a sphere of my own. Anyone know of other places Beryllium Spheres are mentioned? Peace, +Tom

Red-Gate SQL Compare

Every now and then I come across a program that becomes so ingrained in my daily work that I hardly know how I'd get by without it.  I'll probably break down a couple over the next few days, but for database work, I have never found anything as good as Red Gate's SQL Compare and SQL Data Compare .  Essentially these tools let you compare two SQL Server databases (all objects, users, permissions, functions, diagrams, anything) and update changes to whichever database you want.  This is amazingly useful for deploying database changes to a test or production environment (do it to production with ridiculous care, even though it will generate a SQL Script for you and run all updates in one transaction), and making sure everything is synchronized. For releases we can just generate the compare script, confirm that the changes match the updates we want to go out, and store it all in one place with the release details.  This is true for both the structure and the data, to