Warning! This problem is not for the feint of heart. I've spent several days in all trying to troubleshoot it.
I have a Wizard
control with about 5 steps, each with several controls ranging from very simple to very complex such as custom jQuery combo boxes based on DropDownList
s and TextBox
es. Each step has several of the controls wrapped in a PlaceHolder
control.
The Wizard
along with all PlaceHolder
s and their child Controls
are nested inside of a View
of a MultiView
. On the same page I have another View
styled like a Form, but not one. This view has corresponding PlaceHolder
s for each of PlaceHolder
s within each step of the Wizard
.
Depending on the ReferralUrl I call the following function to "Toggle" the view from the Wizard
to the form style view by moving all the controls, then setting the active view as follows:
Protected Sub ToggleView() Handles ViewToggle.Click
If Wizard_mv.ActiveViewIndex = 0 Then
ViewToggle.Text = "Toggle Wizard View"
fPH1.Controls.Add(wPH1)
fPH2.Controls.Add(wPH2)
fPH3.Controls.Add(wPH3)
fPH4.Controls.Add(wPH4)
fPH5.Controls.Add(wPH5)
Wizard_mv.ActiveViewIndex = 1
ElseIf Wizard_mv.ActiveViewIndex = 1 Then
ViewToggle.Text = "Toggle Form View"
wPH1.Controls.Add(fPH1)
wPH2.Controls.Add(fPH2)
wPH3.Controls.Add(fPH3)
wPH4.Controls.Add(fPH4)
wPH5.Controls.Add(fPH5)
Wizard_mv.ActiveViewIndex = 0
End If
End Sub
Immediately after this, I use another function for pre-filling the controls with values from a record in a database. After allowing the users to make some changes, they may resubmit the updated record to the database. The problem is that this works just fine if I do so from the Wizard
but not after toggling. The trace shows that the Control Tree
is empty at the time of submitting the updated record, and hence I cannot grab the user-entered/pre-filled values from this view. The pre-filling works great and the "Selected" values are all correct. The problem arises on the PostBack after clicking submit and it loses all the values and controls.
Please do not answer unless you fully understand my problem and are willing to help. I think the problem very well lies within the page lifecycle. Mysteriously, when I submit from my Wizard, on the postback in Page_Init I get my control values loaded, however when I submit from my form view, neither the controls nor their values are loaded. From what I read, this is an issue of persistence. Really hoping there's a relatively easy solution for this.
Here's a sample of my markup in my Wizard
(all styling removed for brevity):
<asp:WizardStep ID="WizardStep1" runat="server" Title="Product Info">
<asp:PlaceHolder ID="wPH1" runat="server" ViewStateMode="Enabled">
<asp:Table ID="ProductInfoTable" runat="server" Width="100%">
<asp:TableHeaderRow>
<asp:TableHeaderCell><h3>Product Line</h3></asp:TableHeaderCell>
<asp:TableHeaderCell><h3>Publication Type</h3></asp:TableHeaderCell>
<asp:TableHeaderCell><h3>Request Type</h3></asp:TableHeaderCell>
</asp:TableHeaderRow>
<asp:TableRow>
<asp:TableCell>
<div class="ui-widget">
<!-- Autocomplete Combobox -->
<asp:DropDownList ID="productLine_ddl" runat="server" DataSourceID="productLineSDS" ViewStateMode="Enabled" DataTextField="Product" DataValueField="ID"></asp:DropDownList>
<asp:TextBox ID="productLine_cb" runat="server" EnableViewState="True"></asp:TextBox>
<button id="productLine_btn" type="button" title="Show All Items"></button>
</div>
</asp:TableCell>
<asp:TableCell>
<asp:DropDownList ID="docType_ddl" runat="server" DataSourceID="docTypeSDS" DataTextField="DocType" DataValueField="ID"></asp:DropDownList>
</asp:TableCell>
<asp:TableCell>
<asp:DropDownList ID="requestType_ddl" runat="server" DataSourceID="requestTypeSDS" DataTextField="RequestType" DataValueField="ID"></asp:DropDownList>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell ColumnSpan="2">
<asp:MultiView ID="Attachment_mv" runat="server" ActiveViewIndex="0">
<!-- File Upload/Browsing Display -->
<asp:View ID="AttachmentUploadView" runat="server">
<h3 class="inlineH">Attach File: </h3>
<asp:FileUpload ID="AttachmentFile_btn" runat="server" />
<asp:Button ID="UploadFile_btn" runat="server" Text="Upload File" />
</asp:View>
<!-- File Attached Display -->
<asp:View ID="FileAttachedView" runat="server">
<h3 class="inlineH">Uploaded File: </h3>
<asp:Label ID="FileAttachedLabel" runat="server" Text="Label"></asp:Label>
<asp:Literal ID="FilesOnServer" runat="server" />
</asp:View>
</asp:MultiView>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
</asp:PlaceHolder>
</asp:WizardStep>
My Page Lifecycle Events, as requested (in chronological order for your convenience) :)
Dim referrerPage As String
'Initialize Dynamic controls here. These are controls that need to be prefilled, etc.
Private Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Init
'DO NOT PREFILL at this stage, as the Controls are not yet rendered and will cause errors.
' Use this section for detecting the "referrerPage" and missing "id" parameters when expected, etc.
If Not IsPostBack Then
ViewState.Clear()
End If
Try
referrerPage = Right(Request.UrlReferrer.AbsolutePath, Len(Request.UrlReferrer.AbsolutePath) - Request.UrlReferrer.AbsolutePath.LastIndexOf("/"c) - 1)
Catch ex As Exception
If Not String.IsNullOrEmpty(Session.Item("referrerPage")) Then
referrerPage = Session.Item("referrerPage")
End If
End Try
If StrComp(referrerPage, "wizard.aspx") <> 0 And String.IsNullOrEmpty(Session.Item("referrerPage")) Then 'Initialize Session state to remember "referrerPage"
Session.Add("referrerPage", referrerPage)
End If
If StrComp(referrerPage, "formdetails.aspx") = 0 Then
If String.IsNullOrEmpty(Request.Params("id")) Then
'This line checks for an expected "id" param value and if none exists, forwards the page back to "tcom.aspx"
Response.Redirect(Request.UrlReferrer.AbsolutePath)
Else
ToggleView()
End If
End If
End Sub
'Prefill Dynamic controls here.
Private Sub Page_PreLoad(sender As Object, e As EventArgs) Handles Me.PreLoad
If Not IsPostBack Then
productLine_ddl.DataBind()
docType_ddl.DataBind()
requestType_ddl.DataBind()
'...and several more DataBinds for each individual
' control in the wizard. Nothing more.
End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Not IsPostBack Then
'Benign code for querying the database to get User info for Page.User.Identity.Name
End If
End Sub
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As EventArgs) Handles Me.PreRender
If Not IsPostBack Then
'Here is completely benign code for hiding a couple controls.
End If
End Sub
Protected Sub Page_PreRenderComplete(ByVal sender As Object, ByVal e As EventArgs) Handles Me.PreRenderComplete
'PREFILL HERE when appropriate. This occurs after checking the "referrerPage" and ensuring a value for the "id" parameter.
If Not IsPostBack Then
Try
If Not String.IsNullOrEmpty(Request.Params("id")) Then
PrefillWizard(Request.Params("id"))
Else : output.Text += "Source: " + Request.UrlReferrer.AbsolutePath
End If
Catch ex As Exception
End Try
End If
End Sub
To anyone who can help with this, you have my eternal gratitude. ;)
The problem is that the control tree of the page must be exactly the same during every postback. That means that you have to add all the control every time no mater which ActiveViewIndex
is set. Preferably in CreateChildControls
or page init. In the ToggleView
function you can than set the visibility of the controls.