I am trying to wrap my head around the control infrastructure to understand which ones ASP.NET maintains view state for.
There are these regular HTML controls ex:
<input type="radio" checked="checked"/> -> I understand these do not have viewstate
Then there are HTML controls with runat="server"
<input type="radio" checked="checked" runat="server"/> -> Does the viewstate get maintained between postbacks?
Then there are ASP.NET controls
<asp:TextBox id="txtMyText" runat="server"/> -> I understand these do have viewstate
We have a few custom controls that inherit HtmlTextBox
<myPrefix:myTextBox id="txtMyText" runat="server"/> -> Is this the same as type 2 above?
Is it safe to assume that any control with runat="server" tag will have viewstate maintained?
There are 3 types of controls, the standard HTML elements like , HTML server controls which have the runat=server tag added, and full web controls. Only the web controls have viewstate maintained.
When we were having problems with viewstate I started using the Viewstate helper software from Binary Fortress http://www.binaryfortress.com/aspnet-viewstate-helper/
It gives you a real insight into what's going on - as well as helping with viewstate related performance issues you can decode the viewstate with one click and see what is actually in there - so you get to understand what controls are using viewstate and which aren't, and exactly what they are storing in there.
Also, something nobody else has mentioned is ControlState. This came along with ASP.NET 2 and the theory is that the important stuff that is necesssary for a control to function goes in the control state, and the data etc in the viewstate, so you can switch off the viewstate and bind the data to your control on every postback and the control still basically works using controlstate. I say "theory" because in practice the implementation seems patchy. When you look into the dropdownlist code using reflector for example this isn't properly implemented. This may have changed with later releases of the framework, I'm not sure. Lots of info on controlstate out there if you search for it, I just thought I'd mention it.
afaik no, that HTML controls are not designed to maintain anything in the viewstate, if you care about it, take the webcontrols.
Anything you put on your page's view and add the runat="server" will have the viewstate maintained.
As for dynamically added controls, it depends on when and how you add the control to the control tree. Check out the accepted answer to this question, but also check out my question here.
Related
I'm trying to build a very specific search page for a project and I'm having lot of trouble dealing with multiple postbacks invoked by dynamically-generated controls on a single page.
The page has to work like this:
There is a single checkbox, "Detailed search", that causes a postback on checking/unchecking.
When detailed search is not active, a simple grid with contents and buttons is displayed. Nothing special.
When detailed search is active, N checkboxes must be generated from some dynamic data, that represent the sections where you want the search to happen. Below the checkboxes, an AJAX-enabled tab control will appear, initially with no tab pages.
When checking one of the section checkboxes, a postback will occur. After the postback, data will be searched in the section selected by the user, then a new tab page containing a grid view of results and the name of the section will be added to the tab control. If the checkbox is unchecked, the tab page will disappear from the control, again, after a postback.
Now, the issue is that pretty much everything has to be generated dynamically, and that pretty much everything is connected to something else.
First issue: dealing with the "Detailed search" checkbox. Sounds easy, doesn't it? My initial idea was to set Page.Viewstate["DetailedSearchEnabled"] to true or false during the check/uncheck event handler, then create controls dynamically checking the value of DetailedSearchEnabled during Page_Load.
Nope. The postback event-handling happens between Page_Load and Page_LoadComplete. It would take an additional refresh for things to work as intended.
<< Then I'll just generate the controls on Page_LoadComplete! >>
Nope. Those controls need event handling as well, and if they're generated after Page_Load they will not be wired up correctly.
A possible solution would be generating everything in advance, on Page_Load, and only hiding/showing controls on Page_LoadComplete. But that is inefficient, and one important point of this search page is that only the minimum amount of controls should be generated.
The difficulty of this task seems to come from the way event wiring and the page life cycle work.
Surely there must be a better way of approaching this problem.
First issue: dealing with the "Detailed search" check box.
The correct approach (if you want to use page post-backs) is as follows:
In the CheckChanged event handler, save the value of the Checked property to ViewState["DetailedSearchEnabled"]. If the value is true, add the dynamic check boxes to the page. If the value is false, find and remove them.
Override LoadViewState. After calling base.LoadViewState, re-create the dynamic check boxes and wire up their events if ViewState["DetailedSearchEnabled"] is true. Note that neither Page_Load nor Page_LoadComplete is the appropriate place to do this.
Yes, you should create the dynamic check boxes at two points in the page life cycle. I recommend a helper method.
In general, your event handlers should add or remove just the dynamic controls (if any) affected by those particular events, but LoadViewState should re-create all dynamic controls that existed from the previous page request. You must store enough information in view state for LoadViewState to do so.
My answer to this other question demonstrates how to add and remove dynamic controls. You may want to use it as a reference.
Sounds to me like you should be using a CheckBoxList control to handle your dynamic checkboxes. You can add an remove items to the CheckBoxList during your post back and not have to worry about dynamically adding/removing actual controls/events to the form.
Here is a link to the msdn:
https://msdn.microsoft.com/en-us/library/14atsyf5(v=vs.85).aspx
Here is some sample code:
Protected void Button1_Click (object sender, System.EventArgs e)
{
CheckBoxList.Items.Add(new ListItem("TextValue1", "Value1"));
CheckBoxList.Items.Add(new ListItem("TextValue2", "Value2"));
}
If all else fails, you could still fall back on the quick-and-dirty old-fashioned ASP way.
Use Response.Write or <%...%> to generate your dynamic controls as plain old HTML (simple form fields, e.g. <input type="checkbox" name="foo" value="1" />).
Make sure you have a form field for every piece of information you may need after the postback(s). If necessary, use hidden form fields to 're-post' values across subsequent postbacks.
After postback, retrieve the values of the controls with the Request object.
Use those values to adjust the generation of controls as you see fit.
You should be able to do all of this in Page_Load. The advantage is total freedom. The disadvantage is total freedom to make a big mess of your aspx. So you may want to migrate all this dirty code out of your aspx, and into a custom-made control, which you can then add to your aspx.
When generating your own HTML, be careful not to introduce XSS vulnerabilities. Use HtmlEncode where necessary.
As you suggested yourself, there is a better way to tackle it.
If I was in the same situation, I would create web methods for interacting with the page, and use client side to do the UI. I'm currently working mostly with angular JS, although it does come with a learning curve. You could use ng-hide/ng-show to bind to the checkbox event to display the detailed search. When the n number of checkboxes needs to be displayed, you can then just fill them in with ng-repeat, for each of the items you need to display, after a check/uncheck you can dynamically populate new controls etc. through web method calls if extra data is needed.
Pure ASP postbacks are quite clunky from my experience, and not really suited for building a maintainable dynamic UI.
Instead of making so many postbacks, it would be better to use jquery and ajax calls to load the controls as needed and then attach events to it or you can even use UpdatePnael for that. Help Links:
https://www.simple-talk.com/dotnet/asp.net/ajax-basics-with-jquery-in-asp.net/
http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
I am looking for a way to speed up post backs in asp.net. My issue here is that I have a decent amount of items displayed in a telerik RadGrid (a few hundred or so). Each of these items has a detail table. Each parent item and its items in its detail table have checkboxes. When the parent item's checkbox is clicked, it performs a postback and checks or unchecks all of its detail table's items. When I do this, I'm getting about a 1-2 second delay.
A couple limitations:
-Has to be performed server-side: My customer would prefer to keep client-side scripting at a minimum.
-Paging is not an option
There are only a couple things I can think of that would cause a performance hit:
-ViewState size: Although I'm not explicitly storing any values in the viewstate, I think my main issue is the number of items I'm displaying in my grid.
-Hits to the database: When the postback is performed, (aside from membership queries and other business logic) there are no additional calls to the database.
What other options do I have to speed up my postbacks?
EDIT
I forgot to mention that all of my controls are already ajaxified using Telerik's RadAjaxManagerProxy. I have them wrapped in a Panel called pnlContainer, and I'm doing the following:
<telerik:RadAjaxManagerProxy ID="RadAjaxManagerProxy1" runat="server">
<AjaxSettings>
<telerik:AjaxSetting AjaxControlID="pnlContainer">
<UpdatedControls>
<telerik:AjaxUpdatedControl ControlID="pnlContainer" />
</UpdatedControls>
</telerik:AjaxSetting>
</AjaxSettings>
</telerik:RadAjaxManagerProxy>
Maybe there's a better approach?
You can try the following items if you have a big postback happening:
If you have deep control hierarchy (many levels of controls) your IDs will take up a lot of space since each child control's ID is a concatenation of all its parents. Therefore your control IDs should be as small as possible.
When not necessary, dont use .NET controls. For example, if you have a static label, dont use asp:Label but plain SPAN tag.
Use UpdatePanels with ChildrenAsTriggers=true and UpdateMode=Conditional
Minify your javascript and CSS files.
Disable viewstate on items that dont need it.
Enable compression on web server
Try UpdatePanels. You can post back only part of the page that needs it, instead of reprocessing the whole page.
Yes, I realize that's a bit of a vague title but I'm having a hard time stating the problem. I have a .Net .aspx page that has a Master page, some Ajax, and an updatepanel. My problem occurs on 2 different pages but in both cases I'm either selecting a radio button or a checkbox when the behavior occurs. Immediately after selection the entire page moves down. It does not scroll but instead it is like an extra tag was inserted into the source. I have done HTML source comparisons before and after this change and nothing is different. I can only assume it is related to the updatepanel but I cannot determine where this may be happening.
I'd be happy to provide more information if you can direct me towards a solution.
Thanks!
I am not entirely sure if this will do it and there is not enough detail here to be sure, but have you tried setting RenderMode to inline on the UpdatePanel? Maybe that will do the trick. Otherwise go take a look at Fiddler and see what is coming back from the server. Alternate recommendation (if none of the above work) is to just get your result in Json and change the markup yourself with jQuery or something like it.
Incredible. This was exactly the problem. I've modified my code as follows and form stays in place after selection. This was a simple case of selecting a radio button and having it auto-populate a dropdownlist.
[asp:UpdatePanel ID="panelValidation" runat="server" ChildrenAsTriggers="true" UpdateMode="Always" RenderMode="Inline"]
[ContentTemplate]
[asp:ValidationSummary ID="ValidationSummary1" runat="server"]
[ContentTemplate]
[asp:UpdatePanel]
Thanks!
I have a ListView control which is exhibiting an odd behaviour - rows are only partially updating after a postback. I'm hoping someone here can shed some light on why this might be occuring.
My listview DataSource is bound to a List of items which is stored in the page session state. This is intentional, partially to timeout out-of-date views since multiple users view the data. On a plain resort operation, the sorting is handled on page via javascript, and the list/session data order is kept in sync via callbacks. Callback also checks for permissions levels. On a particular resort operation that is more complicated, the javascript on the page makes a postback to the page to handle the sorting logic. The List/Session is updated as in the callback, then the listview control is rebound to the data. The page loads again, and the rows show the new order. No problem, right?
The problem is that some of the elements in the listview do not change value in accordance with the new order. While the hyperlinks and text that is processed on page (ie like <%# Eval("ProjectAbbrev") %>) are updated appropriately, checkboxes, literals, and dropdowns that have their values set via the OnItemDataBound event method are not - they stay "frozen" in place, even though stepping through the code reveals that the method is run during the postback, and that the controls SHOULD be set to their new values. If I go and manually truncate the list to say, half the original size, sure enough only those items are repopulated, but the checkboxes and such still retain their original values.
So my question is: Why aren't these elements updating along with the rest of the listview control elements on the postback? I have the feeling that I'm either misunderstanding the page lifecycle in ASP.NET or that I've encountered a bug of some kind.
At this point I'm thinking I will have to move the more complicated sorting operation to the page in javascript, but that will be rather tricky and I'd like to avoid doing so if possible.
UPDATE: I have tried setting EnableViewState to false and it does not fix this. I couldn't use that tactic in any case because other parts of the page (save) rely on reading the viewstate in the end.
UPDATE: I'm providing some code snippets in the hope that they might shed some light on this issue:
Page: The HyperLink element will update properly after postback, but the CheckBox which has its value assigned in the OnQueueRepeater_ItemDataBound method, will stay the same.
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="TextProcessorProjects.ascx.cs" Inherits="ETD.UI.Controls.TextProcessorProjects" %>
<asp:ListView ID="QueueListView" runat="server" OnItemDataBound="OnQueueRepeater_ItemDataBound">
<ItemTemplate>
<tr>
<td><asp:HyperLink runat="server" ID="ProjectIDLink"><%# Eval("ProjectAbbrev") %></asp:HyperLink></td>
<td><asp:CheckBox runat="server" ID="ScannedCheckBox" BorderStyle="None" /></td>
</tr>
</ItemTemplate>
</asp:ListView>
Code behind: On postback, the following code executes:
protected List<Book> QueueDataItems
{
get { return (List<Book>)Session["Queue"]; }
set { Session["Queue"] = value; }
}
else if (IsPostBack && !Page.IsCallback)
{
// resort QueueDataItems List appropriately
ResortQueue(Request.Params)
// rebind
QueueListView.DataSource = QueueDataItems;
QueueListView.DataBind();
}
protected void OnQueueRepeater_ItemDataBound(object sender, ListViewItemEventArgs e)
{
// ...
// ... other controls set
CheckBox scannedCheckBox = e.Item.FindControl("ScannedCheckBox") as CheckBox;
scannedCheckBox.Checked = book.Scanned;
}
UPDATE: I've given up on getting this to work and moved my sorting logic to the client side with javascript. If anyone has any ideas as to why this odd behaviour was happening though, I'd still be very interested in hearing them!
out of interest, what point on the page are you databinding on? Page_Load?
Try it on OnPreRender - might help out.
Sounds like the ViewState is kicking in and repopulating the data.
In any case, if you are Databinding in every postback anyways, you should probably set the EnableViewState of your ListView to false to reduce page size.
I think this is related to the order of when the different events are triggered during the page lifecycle. See ASP.NET Page Life Cycle Overview. You should do databinding in Page_OnPreRender to make sure the replopulating is done after control events (that will cause data updating) on the page.
Maybe your QueueListView is getting re-bound for some reason.
Try resetting the DataSource value after the DataBind() to see what happens
QueueListView.DataBind();
QueueListView.DataSource = null;
Any chance this could be a caching issue? I ran into a similar issue using a listview with an XMLDatasource. I tried turning off all the viewstate. I would bind the listview in the code behind and use XPath to write it all out to the screen on my aspx page. This was used in a search....the next time I did a search, none of the new information showed up. The reason is because an XMLDatasource has caching enabled by default. In my case I was hitting the DB every time and had no need to cache it. I turned off the caching on the datasource and all my problems were fixed.
I only mention this because the error I was having sounds identical to yours. I can't see how you're retrieving book in the itemdatabound - and you're not using an XML Datasource by the looks of things.....but I thought the comment might trigger something for you. Good luck....though it sounds like you've already moved on :-).
Is the checkbox control ReadOnly=true or Enabled=false?
I have had trouble with controls with one of these properties set as above not updating no matter how I try to manhandle the control in the code behind. I would think disabling the viewstate would also bypass that little "feature" of ASP.NET, but it's worth a shot.
Also, if the items are sorted via clientside code, is that ordering preserved on the postback? I don't think that you can alter the CLR object you have in session via javascript.
Not sure if this helps, replace <%# Eval("ProjectAbbrev") %> with the Hyperlink.Text assigned in the OnQueueRepeater_ItemDataBound method and see if all rows get populated correctly.
This probably won't solve your problem, but will be interesting to know the result.
For the following code:
<% foreach (Entities.Core.Location loc in locations){ %>
<div class="place_meta">
<img src="~/static/images/stars/star_25_sml.gif" runat="server" class="star_rating"/>
</div>
<% }; %>
I would like to display the star rating image for each location object displayed. However, only the first location object's star rating is displayed. For the rest, the image tag becomes <img class="star_rating" />
Am I missing anything in the syntax that allows the ability to have controls with runat=server within a foreach on the aspx page? This is with ASP.net 2.0.
I could possibly call a function in the codebehind or a display class to absolute map the URL but I am very curious if there is a solution to this problem.
Just for clarifications, the path to the image could possibly be different for each location object.
You can't use server controls within an inline loop, because ASP.NET needs to be able to uniquely identify each control to process it. The for... loop prevents this. The easiest and cleanest way is to use a Repeater control and bind your collection to it (in code-behind). Set the URL property in the bind event handler, and maintain a counter variable while binding, to check if you're at the first item or not.
Edit: I've played around with this some more, and found that if you assign an ID attribute, all looped instances get the same ID, but ASP.NET only recognizes the first control found. This would be an extremely dirty way to set properties on the first instance only. Please don't write code like this.
Unless you're using MVC, you might find a Repeater Control more useful in this situation.
If I remember correctly, you can use a source of data (your locations in this instance) and then loop through and set each image.
I don't know if your code is supposed to work, but this one is ;-)
<asp:Image ImageUrl="~/static/images/stars/star_25_sml.gif" runat="server" class="star_rating" />
Perhaps it's what you are looking for.
EDIT: looking at the code of System.Web.UI.HtmlControls.HtmlImage you can see that the src attribute is removed after the (first) processing. This may be the reason why you can get just the first URI correct with your code.
I would remove the runat="server" attribute and reference the image using a relative path (or the full path) instead of using the tilde.
EDIT: FWIW, #miies idea fits the ASP.NET paradigm AND is an example of why I'm switching to ASP.NET MVC.