Databinding in inner .ascx control and FormView
I have ascx control inside FormView. What I would like is use syntax Bind inside ascx. Here is my page:
<asp:ObjectDataSource runat="server" ID="ods" TypeName="MyDS" SelectMethod="Get" UpdateMethod="Update" DataObjectTypeName="Ent">
</asp:ObjectDataSource>
<asp:FormView runat="server" DefaultMode="Edit" ID="fv1" DataSourceID="ods">
<EditItemTemplate>
<uc1:WebUserControl ID="WebUserControl1" runat="server" />
<asp:Button runat="server" CommandName="Update" Text="Update"/>
</EditItemTemplate>
</asp:FormView>
Here is WebUserControl.ascx:
<asp:TextBox开发者_开发百科 ID="txt1" runat="server" Text='<%# Bind("Name") %>' />
On selecting values into the TextBox everything works fine. Bind populates textbox with expected value. But when button "Update" pressed the ObjectDataSource's method Update gets instance of Ent with null instead Name, while entered text is expected. Just for test I have placed textbox into the .aspx and everything works fine.
At last I have decompiled by reflector FormView, where ExtractRowValues looks failed since iterates only over direct children. Does anybody know how to work around with child bindings?
Sorry if this is a bit late :-)
'Bind' is a strange beast. There is no Bind method nowhere. It's just a specific ASP.NET token that instructs the ASP.NET compiler to adds 'Eval' call (there is an Eval method indeed) on databinding and generates a specific extraction code hidden method for dual way binding.
But this method is only added (and called on extraction) for controls that are equipped with ITemplate properties (such as FormView's ItemTemplate, EditItemTemplate, etc...), and that are declared two-ways bindable.
Unfortunately, for a user control (an ascx), there is no easy way to have that kind of method generated.
However, there is the IBindableControl Interface that you could implement in the user control that allows some integration although it's less automatic, less declarative, like this:
WebUserControl.ascx:
<asp:TextBox ID="txt1" runat="server" Text='<%# Eval("Name") %>' />
(Note the Bind is useless so you can just stick with Eval).
WebUserControl.ascx.cs:
public partial class WebUserControl : UserControl, IBindableControl
{
public void ExtractValues(IOrderedDictionary dictionary)
{
dictionary["Name"] = txt1.Text;
}
}
You have to move the <%# Bind("") %> syntax from the user control (ASCX) to the containing page (ASPX) and use properties in your user control to get and set the form values. My example below is adapted from the solution given at http://oudinia.blogspot.co.uk/2007/12/aspnet-20-and-up-c-user-control-within.html with a few more details to try to make it realistic:
ASCX mark-up:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="PaperForm.ascx.cs" Inherits="PaperForm" %>
<asp:ValidationSummary runat="server" HeaderText="<b>Please supply the missing information below.</b>" />
<p class="SectionHeading">Section A: Your Details</p>
<table border="1" width="100%">
<tr>
<td width="220px">Membership number</td>
<td colspan="3"><asp:TextBox ID="txtMemNo" runat="server" MaxLength="9"></asp:TextBox>
<asp:RegularExpressionValidator runat="server" ControlToValidate="txtMemNo" Display="Dynamic" Text="Please check your membership number" ValidationExpression="\d{9}"></asp:RegularExpressionValidator>
</td>
</tr>
<tr>
<td><asp:Label ID="lblFirstName" runat="server">First name</asp:Label></td>
<td><asp:TextBox ID="txtForename" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator runat="server" ControlToValidate="txtForename" Text="Required"></asp:RequiredFieldValidator>
</td>
<td width="110px"><asp:Label ID="lblLastName" runat="server">Last name</asp:Label></td>
<td><asp:TextBox ID="txtSurname" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator runat="server" ControlToValidate="txtSurname" Text="Required"></asp:RequiredFieldValidator>
</td>
</tr>
...
</table>
<script type="text/javascript" language="javascript">
// perform additional client-side validation in JavaScript
function ValidateForm()
{
// check membership number (via web-service)
...
}
</script>
ASCX code-behind:
public partial class PaperForm : UserControl
{
// **public properties representing form data**
public string MemNo { get { return txtMemNo.Text; } set { txtMemNo.Text = value; } }
public string Forename { get { return txtForename.Text; } set { txtForename.Text = value; } }
public string Surname { get { return txtSurname.Text; } set { txtSurname.Text = value; } }
...
protected void Page_Load(object sender, EventArgs e)
{
// prevent browser caching
...
}
}
ASPX mark-up:
<%--register the user control here--%>
<%@ Register TagPrefix="UC" TagName="PaperForm" Src="~/Proposals/PaperForm.ascx" %>
<asp:FormView ID="fvPaper" runat="server" DataKeyNames="PprPropID" DataSourceID="tblPprProp" DefaultMode="Insert" Width="780px">
<InsertItemTemplate>
<%--insert the user control here, **and bind the database fields to its properties**--%>
<UC:PaperForm ID="pf" runat="server" MemNo='<%# Bind("MemNo") %>' Forename='<%# Bind("Forename") %>' Surname='<%# Bind("Surname") %>' ... />
...
<%--can use the JavaScript ValidateForm() function defined in the ASCX file--%>
<asp:Button ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert" Text="Submit" BackColor="#C2D9EC" OnClientClick="return ValidateForm();" />
</InsertItemTemplate>
</asp:FormView>
<%--define the data-source here rather than in the ASCX file--%>
<asp:SqlDataSource ID="tblPprProp" runat="server" ConflictDetection="CompareAllValues" ConnectionString="<%$ ConnectionStrings:confConnectionString %>" OnInserted="AfterInsertion"
InsertCommand="INSERT INTO [tblPprProp] ([MemNo], [Surname], [Forename], ...) VALUES (@MemNo, @Surname, @Forename, ...); SELECT @RowID = SCOPE_IDENTITY()"
OldValuesParameterFormatString="original_{0}">
<InsertParameters>
<asp:Parameter Name="MemNo" Type="String" />
<asp:Parameter Name="Surname" Type="String" />
<asp:Parameter Name="Forename" Type="String" />
...
</InsertParameters>
</asp:SqlDataSource>
ASPX code-behind:
public partial class PaperProposal : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void AfterInsertion(object sender, SqlDataSourceStatusEventArgs e)
{
// get Proposal ID
string rowId = e.Command.Parameters["@RowID"].Value.ToString();
// get e-mail address from ASCX property
string address = ((PaperForm)this.fvPaper.FindControl("pf")).Email;
// send acknowledgement e-mail
...
}
}
精彩评论