FormView Binding Gotcha

Version: ASP.NET 3.5 SP1

This post describes two gotchas with the FormView control when binding:
1) When performing two way databinding, Null values and Nullables get changed to string.Empty in the FormView.
2) Two way binding is not supported for nested controls.

When performing two way databinding, Null values and Nullables get changed to string.Empty in the FormView.
The way the FormView control handles null values has caused me some grief :-(

Consider a FormView bound to an ObjectDataSource like so:

<asp:FormView ID="FormView1" runat="server" DataSourceID="ObjectDataSource1" DefaultMode="Edit">
<EditItemTemplate>
FirstName:
<asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstName") %>' />
<br />
LastName:
<asp:TextBox ID="LastNameTextBox" runat="server" Text='<%# Bind("LastName") %>' />
<br />
<asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
Text="Update" />
</EditItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DataObjectTypeName="Employee"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetEmployee" TypeName="EmployeeWrapper"
ConflictDetection="CompareAllValues" UpdateMethod="SetEmployee"></asp:ObjectDataSource>

The Employee object returned by the ObjectDataSource has LastName set to null like so:
[DataObjectMethod(DataObjectMethodType.Select)]
public Employee GetEmployee()
{
return new Employee()
{
FirstName = "Tom",
LastName = null
};
}
 
Click the Update button on the FormView without changing any properties. In the Form ItemUpdating event, we see that both the OldValues and NewValues have the LastName property set to string.Empty.
(OldValue according to FormView)
FirstName : Tom
LastName :

(NewValue according to FormView)
FirstName : Tom
LastName :
On the other hand, if we try the same thing with the DetailsView control, the LastName will still be “null” on update.

This FormView behavior can be problematic when we we need the original object and the modified object to perform an update with conflict detection (see LINQ to SQL example below). Note that ConflictDetection is set to CompareAllValues in the ObjectDataSource.
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" ConflictDetection="CompareAllValues"
DataObjectTypeName="Customer" OldValuesParameterFormatString="original_{0}" SelectMethod="GetCustomer"
TypeName="CustomerWrapper" UpdateMethod="SetCustomer">
<UpdateParameters>
<asp:Parameter Name="customer" Type="Object" />
<asp:Parameter Name="original_customer" Type="Object" />
</UpdateParameters>
</asp:ObjectDataSource>

public void SetCustomer(Customer customer, Customer original_customer)
{
DataClassesDataContext context = new DataClassesDataContext();
context.Customers.Attach(customer, original_customer);
context.SubmitChanges();
}

In brief, LINQ to SQL compares the original value and new value and generates SQL code to update the original object. Since the original object was modified by the FormView, it will never be found by SQL and no update will occur!

While there are other techniques you can use with Linq to SQL to avoid the issue above, the point of this post is to show how the FormView handles null values.

The workaround is to subscribe to the FormView ItemUpdating event and look for string.empty NewValues and set those to null instead.

protected void FormView2_ItemUpdating(object sender, FormViewUpdateEventArgs e)
{
if (e.NewValues["LastName"].Equals(string.Empty))
{
e.NewValues["LastName"] = null;

}
}

In addition, to keep the OldValues unchanged (if you need it for concurrency purposes), set the DataKeyNames property of the FormView to the properties of the object like so:
<asp:FormView ID="FormView2" runat="server" DataSourceID="ObjectDataSource1" OnItemUpdating="FormView2_ItemUpdating"
DefaultMode="Edit" DataKeyNames="LastName, FirstName">

When the Update button is clicked this time, the null values are retained.
(OldValue according to FormView)
FirstName : Tom
LastName : null

(NewValue according to FormView)
FirstName : Tom
LastName : null
 

Two way binding is not supported for nested controls – AFAIK, this is by design.
See an example below:
<EditItemTemplate>
<asp:UpdatePanel runat="server" ID="up1">
<ContentTemplate>
FirstName:
<asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstName") %>' />
</ContentTemplate>
</asp:UpdatePanel>
LastName:
<asp:TextBox ID="LastNameTextBox" runat="server" Text='<%# Bind("LastName") %>' />
<br />
<asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
Text="Update" />
</EditItemTemplate>

Here we have a TextBox inside an UpdatePanel inside the EditItemTemplate. Since two way binding is not supported for the FirstNameTextBox, the special code for extracting values is not generated resulting in null being sent back for the FirstName property.

To work around this, you will need to subscribe to the ItemUpdating event and extract the values yourself:
protected void FormView2_ItemUpdating(object sender, FormViewUpdateEventArgs e)
{
TextBox FirstNameTextBox = (TextBox)FormView2.FindControl("FirstNameTextBox");
e.NewValues["FirstName"] = FirstNameTextBox.Text;
}

Visibility of controls in a FormView.
Setting Visibility of a control inside a FormView does not work - the TextBox control goes back to visible on update.
protected void Page_Load(object sender, EventArgs e)
{
FormView1.FindControl("FirstNameTextBox").Visible = false;
}

Do it in the DataBound event instead:

protected void FormView2_DataBound(object sender, EventArgs e)
{
FormView1.FindControl("FirstNameTextBox").Visible = false;
}

If you have better ideas on any of the workarounds above, please post a comment or link. Thanks.

1 Comment

  • I'm curious to know why the two-way databinding isn't functional for nested controls. That doesn't seem to make any sense to me. The workaround is fine. I just don't understand why using a nested control doesn't work properly.

    In any case, your blog entry helped me solve my problem, so I wanted to say thanks!

Comments have been disabled for this content.