Viewstate&动态创建UserControls - 首先回发清除控件内容

时间:2013-04-01 15:52:50

标签: asp.net user-controls updatepanel postback

我希望你的无限知识可以帮助我解决这个问题,我找不到任何类似的问题来得到答案。

概述代码是否为tl; dr或反之亦然
我有一系列用户控件,每个用户控件都是一个输入表单,在提交时会生成一封电子邮件并将其内部发送到任何相关位置。所有表单都显示在同一页面上,结构如下。

我有一个更新面板 - 在updatepanel中是一个多视图,其中一个视图包含所有电子邮件表单的用户控件。 这个控件,EmailForms.ascx - 本质上是一个1行,2个单元格表,左侧单元格中有一个树视图,右侧单元格中有一个占位符。树视图中每个叶节点的值包含要在占位符中加载的子电子邮件表单用户控件的URL。加载控件时,如果子电子邮件表单需要回发,则URL将以会话状态存储并在Page_Init中重新加载。

每个电子邮件表单都有自己的提交按钮,ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(btn_Submit)函数调用了Page_Init。如果任何表单有其他需要引发回发的元素,则会以这种方式注册。

问题
一切都几乎完美 - 我的问题特别是第一次回发之后我改变显示哪个子邮件表单,当用户填写表单并点击提交时,所有输入区域都被清除,所有验证器出现错误,不会生成任何电子邮件。每次后续回发都会按原样运行,直到再次将子邮件表单更改为另一个。 要加载的第一个电子邮件表单不会遇到此问题。

任何人都可以提出导致这种行为的原因以及我如何预防这种行为? 这可能是在尝试提交以前加载的表单的视图状态中的值吗?

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

1 个答案:

答案 0 :(得分:2)

这是通过在创建时为每个控件分配ID来解决的。根据子控件的文件路径重新加载 如果不这样做,我假设无论加载哪个控件,自动生成的ID都是相同的,并且viewstate是第一个后续回发的问题,因为存储的控件状态与页面上的控件不匹配。