Wednesday, March 28, 2012

ViewState / PostBacks

Hi all,

Is there any way to update the viewstate actually on the page without having to reload the page?

I know that sounds a bit daft but I would be interested to know - I'm having some issues with my update panels / dynamic controls and this might help.

Hi,

normally the UpdatePanel keeps the viewstate in sync, something a lot of people dislike since that adds significantely to the total data transferred back and forth to the server.

I think your problem is more with the dynamic controls. Be sure to put them in the right eventhandler like Page_Init. Something you can try is to first remove the updatepanel and see if everything works. If it does you can integrate the updatepanel again and recheck.

Grz, Kris.


Hi Kris,

Thank you for your reply.

When you say that the update panel keeps the viewstate in sync, should I notice this on the page if I view source?

For example - lets say to keep things easy when my page loads I view source and there's 100 characters of viewstate, I then perform an action that'll change this, are we saying that the update panel has changed the page so that if I view source now I might see 150 characters for example?

My object that is held in viewstate I can access programmatically, ie, repopulate my object when needed from viewstate and everything is there - I was just wondering whether the actual characters in the view source are going to change or only if there's a full postback?


I doubt you'll see it in a ViewSource; I don't think most browsers reevaluate that after an async postback. You'll want to use the IE Dev toolbar or the Firefox Dom Explorer to get a runtime view of what's in there.


Hi,

another great tool would beFiddler.

Grz, Kris.


Thanks for the info about the additional tools chaps - appreciated.

My problem after further diagnosis seems to be this...

When a user adds a resource, I add the drop down menus using AJAX, the update panel updates, I store the select resource in the view state so that when the update panel updates again I can call back the already selected ones to dynamically rebuild those controls again.

My problem seems to be that when you select an item in the list it doesnt fire the onselectedindexchanged event, I'm completely at a loss why to be honest because I'm using the same function for the controls that are added to the update panel after the user has chosen a resource as I am for the pre-selected resources that come with a role - and thats working?

Any thoughts? I've attached a screenshot so you can perhaps get a better idea of what I'm trying to achieve...

Application Screen Shot


Well, I'm not sure how to solve the problem offhand; I tend to limit my use of updatepanels where possible. I'm not entirely sure at what point you're adding your controls based on your description; are you adding them using javascript on the client? That's what it sounds like. if that's the case, then you're not going to be able to do what you're trying to do b/c they don't exist on the server. If you're adding them on the server during the updatepanel's firing, then I'm pretty sure the upd won't recognize them (iirc, it loads all its triggers, etc, on the initial load during Init or PreInit of the page). You might be able to manually force an update by using the PageRequestManager.update() function on the client, and then wire the dropdown's event handler to a function that calls that; but I'm just guessing at this point.


Hi Paul, thank you for your reply...

To clarify what I'm trying to do then...

Looking at the screenshot above you'll notice two clear sections "Pre-selected resources" and "Additional resources".

My code gets the data for the role from SQL, and builds the 3 sets of drops downs (for access level and further detail) adding them to a table control.

I have them also in independent updatePanels, ie, one around each set of drop downs, when the user selects and access level in the first drop down menu (on the left) it'll run my code server side to update the items in the second drop down menu (on the right).

So in the screenshot, the top half of the page is working as expected, and also includes validators etc if nothing is selected on either of the menu's (left or right).

The "additional resources" is where it got more difficult...

When the user clicks on the "Add Resource Hyperlink" a popup window is launched which displays a list of all available resources as hyperlinks, each of the hyperlinks has a javascript call wired up so that when they are clicked it will place the ID of the resource into a hidden text box on the parent window. It also "clicks" a hidden button on the parent window.

My code then detects the click even and grabs the resource id from the hidden text field. I use this to then go and get all of the access levels for the resource and add a set of drop down menus to the "additional resources" part of the page, this has its own containing updatePanel.

The set of drop down menus are added with their own updatePanel as per the "pre-selected resources" section, the code to generate these is a generic function used by both.

I am then expecting the onSelectedIndexChanged event on the access level drop down (on the left) to fire when the user selects an item which in turn should then run my code to populate the further detail drop down menu (on the right) - this is what is currently not working - there are no errors, server or client side - simply nothing happens.

Initially I had a problem where I was adding the resource (with the drop downs) to the update panel and when I added the next one the first disappeared, this turned out to be because the server did not know anything about them as they were added client side, I got around this by changing my onSelectedIndexChanged code to add the selected resource to my userRegistration object held in viewstate, and then each time it updates the update panel it rebuilds the controls, thus any previously selected ones appear.

I hope this helps to clarify what I'm trying to do and where it isn't working, if it helps I'm happy to take a few more screenshots for each of the steps of the process to help further...


Any chance I can see the code related to those additional drop downs? PM is fine if you're not comfortable posting it here.

Also, use Fiddler to see if the event fires, but is not handled by the server, or it doesn't fire at all.

If it's fired, but not handled by the server, then you might have to do some manual inspection of the request to see if one of those dynamic controls fired it and then manually call the handler. If it's not fired, then you might have to do some .js hotness to get the controls properly wired up.

Paul


Hi Paul,

Thanks for your reply - no problem posting the code up - no need to PM - hadn't done it thus far as there's quite a bit of it...anyway...here goes...this should be most of the relavent stuff I think - if there's anything else let me know - last night I got as far as when you change the selected item in the access level (left hand drop down) nothing happens, but then on the second time you change the selection it fires something - not sure what as it then didn't display any controls - lol...looks like something might be nearly working...maybe...

Private Sub PopulateRoleBasedResources()' declare variablesDim userRegistrationAs UserRegistrationDim roleResourceCollectionAs RoleResourceCollection.SerializableRoleResourceCollectionDim roleResourceAs RoleResource.SerializableRoleResourceDim addSpacerAs Boolean' instantiate userRegistration =New UserRegistration(ViewState) roleResourceCollection =New RoleResourceCollection.SerializableRoleResourceCollection(userRegistration.RoleID)' iterateFor Each roleResourceIn roleResourceCollection' populate _iteration += 1' check position in collectionIf roleResourceCollection.AtEndOfCollection =True Then' populate addSpacer =False Else' populate addSpacer =True End If' create table rows for resource access levels and parameter options CreateTableRowsForResourceAccessLevelAndParameterOptions(AccessLevelParameterOptionsType.PreSelected, tblRegistrationPreSelectedResources, _iteration, roleResource, roleResource.AccessLevels, addSpacer)Next End Sub Protected Sub ddlResourceAccessLevelSelectedIndexChanged(ByVal senderAs Object,ByVal eAs System.EventArgs)Dim dropDownListAs DropDownListDim resourceAccessLevelIDAs GuidDim controlIterationAs String Dim requiredFieldValidatorAs RequiredFieldValidator' populate controlIteration ="" dropDownList = sender resourceAccessLevelID =New Guid(dropDownList.SelectedItem.Value.ToString())Select Case sender.id.ToString.Contains("AccessLevel")Case True controlIteration = Right(sender.ID.ToString, (Len(sender.ID.ToString) - 23))Case False controlIteration = Right(sender.ID.ToString, (Len(sender.ID.ToString) - 21))End Select' find our resourceParameter drop down list dropDownList = Page.FindControl("ddlResourceParameter_" & controlIteration)' populate PopulateResourceParameters(resourceAccessLevelID, dropDownList)' find our resourceAccessLevel requiredFieldValidator requiredFieldValidator = Page.FindControl("vldResourceAccessLevel_" & controlIteration)' validate requiredFieldValidator.Validate()End Sub Private Sub PopulateResourceParameters(ByVal resourceAccessLevelIDAs Guid,ByRef dropDownListAs DropDownList)' declare variablesDim sqlAs SQLDim resourceParametersAs Data.DataTableDim listItemAs ListItem' check resourceAccessLevelIDIf resourceAccessLevelID.ToString <>"00000000-0000-0000-0000-000000000000"Then' instantiate sql =New SQL' populate resourceParameters = sql.GetResourceAccessLevelParameters(resourceAccessLevelID)' check we returned some resourceParametersIf resourceParameters.Rows.Count > 0Then' populate dropDownList.DataSource = resourceParameters dropDownList.DataTextField = resourceParameters.Columns("ResourceParameterName").ColumnName.ToString() dropDownList.DataValueField = resourceParameters.Columns("ResourceParameterID").ColumnName.ToString() dropDownList.DataBind()' instantiate listItem = CreateListItem("00000000-0000-0000-0000-000000000000","Please Select") listItem.Selected =True' add list item to drop down list dropDownList.Items.Insert(0, listItem)Else' instantiate listItem = CreateListItem("00000000-0000-0000-0000-000000000000","None Available") listItem.Selected =True' add list item to drop down list dropDownList.Items.Add(listItem)' TODO: Send error to webmaster if no rolegroups have been loadedEnd If' housekeeping ' Dispose(sql)Else' clear all previously added resourceParameters dropDownList.Items.Clear()End If End Sub Protected Sub ddlResourceParameterSelectedIndexChanged(ByVal senderAs Object,ByVal eAs System.EventArgs)' declare variableDim controlIterationAs Integer Dim requiredFieldValidatorAs RequiredFieldValidator' populate controlIteration = Right(sender.ID.ToString, (Len(sender.ID.ToString) - 21))' find our resourceAccessLevel requiredFieldValidator requiredFieldValidator = Page.FindControl("vldResourceParameter_" & controlIteration)' validate requiredFieldValidator.Validate()End Sub Private Sub CreateTableRowsForResourceAccessLevelAndParameterOptions(ByVal accessLevelParameterOptionsTypeAs AccessLevelParameterOptionsType,ByRef tableAs Table,ByVal iterationAs Integer,ByRef enumeratorAs Object,ByRef enumerableObjectAs Object,ByVal addSpacerRowAs Boolean)' declare variablesDim tableRowAs TableRowDim tableCellAs TableCellDim dropDownListAs DropDownListDim listItemAs ListItemDim updatePanelAs UpdatePanelDim requiredFieldValidatorAs RequiredFieldValidatorDim requiredFieldValidator2As RequiredFieldValidatorDim accessLevelDropDownListIDAs String Dim parameterDropDownListIDAs String Dim accessLevelValidatorIDAs String Dim parameterValidatorIDAs String Dim resourceAccessLevelAs ResourceAccessLevel.SerializableResourceAccessLevel' populate resourceAccessLevel =Nothing accessLevelDropDownListID ="ddlResourceAccessLevel_" & iteration accessLevelValidatorID ="vldResourceAccessLevel_" & iteration parameterDropDownListID ="ddlResourceParameter_" & iteration parameterValidatorID ="vldResourceParameter_" & iteration' *** First table row *** ' *** First table cell *** ' instantiate tableRow = CreateTableRow() tableCell = CreateTableCell(40, HorizontalAlign.Left)' populate AddWebControlToControlCollection(tableCell, CreateImage(16, 1,"UI/Images/Misc/Shim.gif","","")) AddWebControlToControlCollection(tableCell, CreateImage(16, 16,"UI/Images/Misc/BulletPoint.gif","Bullet Point","*")) AddWebControlToControlCollection(tableCell, CreateImage(5, 1,"UI/Images/Misc/Shim.gif","",""))' add tablecell to tablerow tableRow.Cells.Add(tableCell)' *** First table row *** ' *** Second table cell *** ' instantiate tableCell = CreateTableCell(HorizontalAlign.Left)' populate tableCell.Text = enumerator.Name tableCell.Style.Add("font-weight","bold")' add tablecell to tablerow tableRow.Cells.Add(tableCell)' *** First table row *** ' *** Third table cell *** ' instantiate tableCell = CreateTableCell(HorizontalAlign.Left)' populate tableCell.Text =" "' add tablecell to tablerow tableRow.Cells.Add(tableCell)' *** First table row *** ' *** Fourth table cell *** ' instantiate tableCell = CreateTableCell(HorizontalAlign.Left)' populate tableCell.Text =" "' add tablecell to tablerow tableRow.Cells.Add(tableCell)' add tablerow to table table.Rows.Add(tableRow) tableRow =Nothing tableCell =Nothing' *** Second table row *** ' *** First table cell *** ' instantiate tableRow = CreateTableRow() tableCell = CreateTableCell(40, HorizontalAlign.Left)' populate tableCell.Text =" "' add tablecell to tablerow tableRow.Cells.Add(tableCell)' *** Second table row *** ' *** Second table cell *** ' instantiate tableCell = CreateTableCell(HorizontalAlign.Left) updatePanel =New UpdatePanel dropDownList =New DropDownList listItem =New ListItem("Please Select","00000000-0000-0000-0000-000000000000") listItem.Selected =True' populate updatePanel.ID ="upnAccessLevelParameter_" & iteration.ToString updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional updatePanel.RenderMode = UpdatePanelRenderMode.Inline dropDownList.AutoPostBack =True dropDownList.ID = accessLevelDropDownListID dropDownList.CssClass ="vam" listItem.Selected =True' create event handler ' AddHandler dropDownList.SelectedIndexChanged, AddressOf ddlPreSelectedResourceAccessLevelSelectedIndexChangedAddHandler dropDownList.SelectedIndexChanged,AddressOf ddlResourceAccessLevelSelectedIndexChanged' add listitem to dropdownlist dropDownList.Items.Add(listItem)'iterateFor Each resourceAccessLevelIn enumerableObject' instantiate listItem =New ListItem(resourceAccessLevel.Name, resourceAccessLevel.ID.ToString)' add listitem to dropdownlist dropDownList.Items.Add(listItem)Next' instantiate requiredFieldValidator = CreateRequiredFieldValidator(dropDownList.ID.ToString, accessLevelValidatorID,"00000000-0000-0000-0000-000000000000","<img class=""vam"" src="http://pics.10026.com/?src="UI/Images/Icons/Warning.gif"" style=""width:16px;height:16px;"" title=""Required information missing"" alt=""!"" />","Required information","UserRegistrationFurtherDetailStep3")' populate updatePanel.ContentTemplateContainer.Controls.Add(dropDownList) updatePanel.ContentTemplateContainer.Controls.Add(CreateImage(16, 16,"UI/Images/Misc/MandatoryField.gif","Required information","Required information","vam", 0, 0)) updatePanel.ContentTemplateContainer.Controls.Add(requiredFieldValidator) updatePanel.ContentTemplateContainer.Controls.Add(CreateImage(16, 16,"UI/Images/Icons/Information.gif","Click to view Access Level information","Information","vam", 5, 35))' instantiate dropDownList =New DropDownList' populate dropDownList.AutoPostBack =True dropDownList.ID = parameterDropDownListID dropDownList.CssClass ="vam"' create event handlerAddHandler dropDownList.SelectedIndexChanged,AddressOf ddlResourceParameterSelectedIndexChanged' instantiate requiredFieldValidator2 = CreateRequiredFieldValidator(dropDownList.ID.ToString, parameterValidatorID,"00000000-0000-0000-0000-000000000000","<img class=""vam"" src="http://pics.10026.com/?src="UI/Images/Icons/Warning.gif"" style=""width:16px;height:16px;"" title=""Required information missing"" alt=""!"" />","Required information","UserRegistrationFurtherDetailStep3") requiredFieldValidator2.IsValid =True' populate updatePanel.ContentTemplateContainer.Controls.Add(dropDownList) updatePanel.ContentTemplateContainer.Controls.Add(CreateImage(16, 16,"UI/Images/Misc/MandatoryField.gif","Required information","Required information","vam", 0, 0)) updatePanel.ContentTemplateContainer.Controls.Add(requiredFieldValidator2)' populate AddWebControlToControlCollection(tableCell, updatePanel)' add tablcell to tablerow tableRow.Cells.Add(tableCell)' *** Second table row *** ' *** Third table cell *** ' instantiate tableCell = CreateTableCell(HorizontalAlign.NotSet)' populate tableCell.Controls.Add(CreateImage(16, 16,"UI/Images/Icons/Information.gif","Click to view Further Detail information","Information","vam", 5, 5))' add tablecell to tablerow tableRow.Cells.Add(tableCell)' instantiate tableCell = CreateTableCell(HorizontalAlign.Left)' *** Second table row *** ' *** Fourth table cell *** ' populate tableCell.Text ="Empty cell"' add tablecell to tablerow tableRow.Cells.Add(tableCell)' add tablerow to table table.Rows.Add(tableRow)' add a spacer if requiredIf addSpacerRow =True Then' instantiate tableRow = CreateTableRow() tableCell = CreateTableCell(2, HorizontalAlign.Left)' populate tableCell.Text =" "' add tablecell to tablerow tableRow.Cells.Add(tableCell)' add tablerow to table table.Rows.Add(tableRow)End If End Sub

Ok - I've had some success!!

I ran it through in debug mode with a break point on the page load, this is where I have a test to see if we're posting back asynchronously and build the page depending on some factors.

What I noticed was that when I call my onSelectedIndexChanged event handler function I add the resource to the userRegistration object and then rebuild the controls on the page (iterating through those in my object) - but when I do any other form of postback (even if asynchronously) I wasn't calling this - and this was why they seemed to disappear.

So, I've amended the code to incorporate this and now I have the controls not disappearing, I've even managed to get them to fire and populate the further detail drop down menu (on the right) - the only minor thing to resolve now is where this is call from - as if I leave the code in the onSelectedIndexChanged event handler function then my controls get duplicated on the page, if I take it out then all but the most recently added one appear but I'm hoping this will be easy to resolve - get there though - and even my validators work - lol - gotta love OO!Big Smile


Glad I could help. seems like you'd want to pull the code in your onSelectedIndexChanged handler out into a helper function, and then have the OSIC handler perform inspection of the sender to determine whether to fire that version, or a different version that doesn't create duplicates. Alternatively, if you keep (as you suggested) your created 'stuff' in the state bag, then you should just be able to inspect the contents of the state bag to see what's there, and then add to it as needed, rather than blindly adding, if that make ssense.


Hi Paul,

Ok - I'm still struggling a bit with this...

The controls are being added which is a good thing - however, if I add one control, I can then use it and the OSIC event fires - great...

If I add more than one control then you seem to need to select 2 different values before it fires...I've followed this through in the debugger and I have a theory - but thats about it!

At the moment I've got the code that iterates through the resources in the userRegistration object as a separate sub, I call this when the page loads (based on certain criteria), so that all of the items are displayed, part of this process is to clear the table first, thus anything thats there is cleared, then all of the resources are rendered.

When the user adds a new one I call this sub again, as obviously I need to show the one they just added...

It looks as if the building of the controls twice might be causing the problem, whilst they have ID's that are set by me I get the feeling that its losing some reference from what had the OSIC event on the first fire, ie, nothing happens, then the page is rebuilt and the second time it seems to work - its very strange...

I'm not sure of the best approach now - I need to display whatever resources have already been added AND the one that has just been added and obviously have all of them firing correctly...

Any thoughts?


Hi Rob; got your PM; sorry I didn't respond earlier.

I'm not really sure based on your code what might be causing the problem; can you look at the rendered' page's source through a runtime tool like the IE developer toolbar and check the DOM tree and the attributes of each of your ddls to see what they look like at each step of the operation? If you have the site somewhere that's accessible, I'll look at it that way for you.

I'm also not entirely clear on the behavior you're describing. Let me repeat my undertanding and you can tell me where I'm mistaken: What you're experiencing is that when you have one dynamic dropdown on the page all is well. That is a ddl that is added after the initial page load. When you later add a second dynamic control, the first time you make a selection on it, nothing happens. the second time you make a selection on it, you get the expected behavior. Is that correct?


Hi Paul,

No worries, I just thought I'd give you a try on this as you helped me out last timeSmile

I dont have the developer tool bar installed, so I'll get it and install it, I've never used it before so I hope its intuitive - you've seen what I mess I can get into! hehe...

I think your description of your understanding was correct - albeit that obviously each time I try to fix this I change something and then that changes the outcome, but I typically then put it back to how it was and go around in circles...

I can confirm that if you only add ONE additional resource it seems to work perfectly, but I suspect that this is because the method to build the controls is being called twice, once in the page load, and once on the button click event, well actually 3 times then - the page loads, it tries to iterate through the collection of already selected resources but there aren't any, so its doesnt, then you select one, the javascript clicks the button, the ajax enables the .net method to be called which adds a resource object to my collection in the userRegistration object in viewstate, and then calls the method to build the controls. No doubt at some point here there's been a postback either full or async - async most likely, thus the same call to build the controls would have happened there too - thus I believe its called twice...maybe thats why if you only add one it works..

I have a line of code in the build mehtod which clears all rows from the current table, this was added to ensure that I didn't get duplicates on the page, if I remove this then I do get duplicates so felt it best to keep this in, so each time it will remove everything from the table, and then rebuild.

If I remove the call to build from the add resource method (fired from the button click) then I never see the most recently added object on the page, my code has added it to the viewstate though so on the next postback/async postback the build method is called and they all appear.

Regarding the number of times you have to select something in the drop downs my theory is that when its say the second control, its run the build method AFTER the page_init, .net doesn't know about the control althought it was added programmatically and is on the page - and thus I believe (probably wrong) it equally doesn't know about the eventhandler (made programmatically with addHandler) - therefore when I select an option it doesnt fire my event, instead it does the autopostback because this is set to true to try and enable the AJAX etc...as such when the postback is called it runs my build method which then takes the resources from the collection in my userRegistration object from viewstate and builds the controls - I think (although have almost lost the plot now) - at this stage the selected index change event DOES work because .net now knows about the controls...and as such the second drop down menu is populated...

I think thats about it...

No comments:

Post a Comment