Reputation: 1832
I'm hoping your infinite knowledge can help me out with this one, I couldn't find any questions similar enough to get an answer.
Overview if the code is tl;dr or vice versa
I have a series of user controls, each one is an input form that when submitted generates an email and sends it internally to whatever relevant location. All the forms appear on the same page, structured as follows.
I have an updatepanel - within the updatepanel is a multiview and one of the views houses a user control for all email forms.
This control, EmailForms.ascx - is essentially a 1 row, 2 cell table with a treeview in the left cell and a placeholder in the right cell. The values of each leaf node in the treeview hold the URL's of the child email form user controls to be loaded in the placeholder. When a control is loaded, the URL is stored in session state and reloaded in Page_Init
if the child email form needs to postback.
Every individual email form has it's own submit button, with ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(btn_Submit)
called in the Page_Init
function. If any form has other elements that need to cause postbacks, they are registered this way.
The Issue
Everything almost works perfectly - my problem is specifically the first postback after I change which child email form is displayed, when the user fills out the form and hits submit, all the input areas are cleared, all the validators come up as false and no email is generated. Every subsequent postback works as it should do until changing the child email form to another again.
The first email form to load doesn't suffer this problem.
Can anyone suggest what's causing this behaviour and how I go about preventing it? Could this be trying to submit the values that were in the viewstate of the previously loaded form?
Default.aspx
<body>
<form id="MainForm" runat="server">
<asp:ScriptManager ID="NavigatorScriptManager" runat="server" EnablePartialRendering="true">
</asp:ScriptManager>
<!-- irrelevant stuff omitted -->
<asp:UpdatePanel ID="upd_Pan1" runat="server" >
<ContentTemplate>
<asp:MultiView ID="multi_MainRealContent" runat="server" ActiveViewIndex="0" >
<!-- Other views are fine -->
<asp:View runat="server" ID="View4">
<asp:Panel runat="server" ID="Panel5" CssClass="ViewContainer LeftAlignment">
<UsrCtrl:EmailForms ID="Sp_Emails" runat="server" width="95%" />
</asp:Panel>
</asp:View>
</asp:MultiView>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
EmailForms.ascx
<asp:Table runat="server" ID="tbl_ToolSelect" Width="100%" CellSpacing="0" CssClass="LayoutTable EmailFormsTable">
<asp:TableRow ID="TableRow1" runat="server">
<asp:TableCell ID="TableCell1" runat="server" Width="300px">
<asp:TreeView runat="server" ID="tree_EmailForms"
DataSourceID="xml_EmailForms"
ExpandDepth="0" ShowLines="false"
NodeIndent="10" NodeWrap="True" PopulateNodesFromClient="false"
ShowExpandCollapse="True" >
<DataBindings>
<asp:TreeNodeBinding DataMember="Pillar" TextField="id" SelectAction="None" PopulateOnDemand="False" />
<asp:TreeNodeBinding DataMember="Group" TextField="id" SelectAction="None" PopulateOnDemand="False" />
<asp:TreeNodeBinding DataMember="Form" TextField="id" ValueField="url" SelectAction="Select" PopulateOnDemand="False" />
</DataBindings>
</asp:TreeView>
</asp:TableCell>
<asp:TableCell ID="TableCell2" runat="server" >
<asp:PlaceHolder runat="server" ID="Ctrl_Placeholder"></asp:PlaceHolder>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
<asp:XmlDataSource ID="xml_EmailForms" runat="server" DataFile="EmailFormHeirarchy.xml" XPath="ROOT/*"></asp:XmlDataSource>
EmailForms.ascx.vb
Protected Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
If (Not Session Is Nothing) AndAlso (Not Session("LastControl") Is Nothing) Then 'ONLY if we have selected an email form previously
'reload the UserControl
Dim ctrl As UserControl = LoadControl(Session("LastControl"))
Ctrl_Placeholder.Controls.Add(ctrl)
End If
End Sub
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(tree_EmailForms)
Page.ClientScript.RegisterClientScriptBlock(Me.GetType, "EmailFormsCSS", "<link href='EmailForms/EmailForms.css' rel='stylesheet' type='text/css' />")
End Sub
Protected Sub tree_EmailForms_SelectedNodeChanged(sender As Object, e As System.EventArgs) Handles tree_EmailForms.SelectedNodeChanged
Try
Ctrl_Placeholder.Controls.Clear()
Dim ctrl As UserControl = LoadControl(tree_EmailForms.SelectedNode.Value)
Ctrl_Placeholder.Controls.Add(ctrl)
Session("LastControl") = tree_EmailForms.SelectedNode.Value
Catch ex As System.Exception
//Occasional viewstate errors if there has been a long time between postbacks
SharedFunctions.LogError(Me.Page, ex.Message, ex.StackTrace)
End Try
End Sub
GenericChildEmailForm.ascx
<div class="descriptionArea">
<h2 class="centered">FORM HEADING</h2>
<p>FORM DESCRIPTION</p>
</div>
<div runat="server" id="inputArea" class="inputArea">
<asp:Table ID="inputTable" runat="server" CssClass="inputTable">
<asp:TableRow>
<asp:TableHeaderCell CssClass="LeftColumn">
<asp:Label runat="server" Text="FIELD HEADING"></asp:Label>
<asp:RequiredFieldValidator runat="server" ID="val_req_ID" ControlToValidate="ID" Display="Dynamic" EnableClientScript="true" ErrorMessage="MESSAGE"> *</asp:RequiredFieldValidator>
</asp:TableHeaderCell>
<asp:TableCell CssClass="RightColumn">
<asp:Textbox runat="server" id="ID" width="98%"></asp:textbox>
</asp:TableCell>
</asp:TableRow>
<!-- repeat the above table row as required -->
<asp:TableRow>
<asp:TableCell ID="cell_HR" ColumnSpan="2">
<div style="width:98%"><hr /></div>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell ID="cell_Submit" ColumnSpan="2">
<asp:Button runat="server" ID="btn_Submit" Text="Submit" Width="98%" Height="30px" CausesValidation="true" />
</asp:TableCell>
</asp:TableRow>
</asp:Table>
</div>
<div runat="server" id="errorArea" class="errorArea">
<asp:ValidationSummary runat="server" ID="val_Sumamry" DisplayMode="BulletList" ShowMessageBox="true" ShowSummary="false" />
</div>
<div runat="server" id="outputArea" class="confirmationArea">
</div>
GenericChildEmailForm.ascx.vb
Protected Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(btn_Submit)
End Sub
Protected Sub btn_Submit_Click(sender As Object, e As System.EventArgs) Handles btn_Submit.Click
//this function iterates over the input table and extracts the value fields of whatever controls appear in the cells. <hr> and submit rows excluded.
Dim emailBody As String = SharedFunctions.PrepareEmailBody(inputTable, Me.Page)
SharedFunctions.Send_Email("toAddress", "fromAddress", "displayName", "subject", emailBody)
outputArea.InnerHtml = "<h3>Email Sent</h3><p>A copy for your notes...</p><hr />" & emailBody
LogUsage() //in local database
SharedFunctions.ClearForm(FindControl("inputArea"))
End Sub
Upvotes: 2
Views: 712
Reputation: 1832
This was solved by assigning an ID to each control on creation & reload, based on the file path of the child control.
Without doing this, I assume the automatically generated ID was the same regardless of which control is loaded, and the viewstate was the problem on the first subsequent postback as the control state stored did not match the control on the page.
Upvotes: 2