What choices do I have for creating stateful dynamic content in an ASP.Net web site?
Here's my scenario. I have a site that has multiple, nested content regions. The top level are actions tied to a functional area Catalog, Subscriptions, Settings.
When you click on the functional action, I want to dynamically add content specific to that action. For example, when Catalog is clicked, I want to display a tree with the catalog folders & files, and a region to the right for details.
When a user clicks on the tree, I want a context sensitive details to load in the details region (like properties or options to manage the files).
I started with UserControls. They worked fine as long as I kept loading everything into the page, and never let one disappear. As soon as one disappeared, ViewState for the page blew up because the view state tree was invalid.
(I didn't want to keep loading stuff into my page because I don't want the responses to be too huge)
So, my next approach was to replace my dynamic regions with IFrames. Then instead of instantiating a UserControl, I would just change the source on my IFrame. Since the contents of the IFrames were independent pages I didn't run into any ViewState problems.
But, I'm concerned that IFrames might be a bad design choice, but don't fully understand why. The site is not public, so search engines aren't a concern.
So, finally to my question.
What are my options for this scenario? If I choose an Ajax Solution (jQuery), will I have to maintain my own ViewState? Are there any other considerations I should take into account?
Controls that are added dynamically do not persist in viewstate, and this is the reason that it doesn't matter if you use AJAX or iframes or whatever.
One possible work-around is to re-populate controls on postback. The problem with this, is the page life-cycle (simplified) is:
Initialize
LoadViewState
Load Postback Data
Call control Load events
Call Load event
Call control events
Control PreRender
PreRender
SaveViewState
Unload
What this means is the only place to re-add your dynamic controls is Initialize -- otherwise posted data (or viewstate information) is not loaded into that control. But often, because Viewstat/postback data isn't available yet in Initialize, your code doesn't have the information it needs to figure out which controls need to be added.
The only other work-around I've found in this situation is to use a 3rd party control called DynamicControlsPlaceholder. This works quite well, and persists the control information in viewstate.
In your particular case, it doesn't seem like there are that many choices/cases. Is it practical just to have all the different sets of controls in the page, and put them inside of asp:placeholder controls, and then just set one to visible, depending on what is selected?
Some other options:
Content only appears to be dynamic. You load enough controls on the page to handle anything and only actually show what you need. This saves a lot of hassle messing with view state and such, but means your page has a bigger footprint.
Add controls to the page dynamically. You've already been playing with this, so you've seen some of the issues here. Just remember that the place to create your dynamic controls for postbacks is in the Page_Init() event, and that if you want them to be stateful, you need to keep that state somewhere. I recommend a database.
you've got a number of different options, and yes, IFrames were a bad design choice.
The first option is the AJAX solution. And with that there's not really a viewstate scenario, it's just you're passing data back and forth with the webserver, building the UI on the fly as needed.
The next option is to dynamically add the controls you need for a given post, everytime. The way this would work, is that at the start of the page life cycle, you'd need to rebuild the page exactly as it was sent out the last time, and then dump out all the unneeded controls, and build just those that want.
A third option would be to use Master pages. Your top level content could be on the Master page itself, and have links to various pages within the website.
I'm sure given enough time, I could come up with more, but these 3 appeared just from reading your problem.
dynamic controls and viewstate don't mix well, as noted above - but that is a Good Thing, because even if they did the viewstate for a complex dynamic page would get so bloated that performance would diminish to nil
use Ajax [I like AJAX PRO because it is very simple to use] and manage the page state yourself [in session, database tables, or whatever works for your scenario]. This will be a bit more complicated to get going, but the results will be efficient and responsive: each page can update only what needs to change, and you won't be blowing a giant viewstate string back and forth all the time
Related
Hello I am trying to lear how to create Dynamic User Controls in asp.net.
I just know that this type of controls are created or loaded at run time.
Someone knows a good tutorial about this topic?
thanks in advance,
The best thing you can learn about dynamic controls in ASP.Net webforms is how to avoid them. Dynamic controls in asp.net are filled with pitfalls. I almost always recommend one of the following alternatives:
Place a reasonable fixed number of controls on the page, and then only show the ones you need.
Figure out the source for the dynamic controls and abstract it out to a datasource (array, ienumerable, list, etc) that you can bind to a repeater, even if it's just a call to Enumerable.Range().
Build a user control that outputs the html you want, bypassing the entire "controls" metaphor for this content.
If you really must work with dynamic controls, it's important to keep the stateless nature of http in mind, along with the asp.net page life cycle. Each adds it's own wrinkle to making dynamic controls work: the former that you need to create or recreate the controls every time you do a postback, and the latter that you need to do this before hitting the page load event - usually in page init or pre-init.
Typically what people are talking about here is the dynamic instantiation and addition of a control to a placeholder.
For example
Control ControlInstance = LoadControl("MyControl.ascx");
myPlaceholder.Controls.Add(ControlInstance);
The above instantiates MyControl.ascx and places it inside of a placeholder with id of myPlaceholder.
I agree with #Joel by knowing the page lifecycle, the stateless nature in mind etc it is possible to avoid the pitfalls. The main things to watch out for, which I have had to do, are:
Page_Init – initialise the controls that are on the page here as they were the last time you rendered the page. This is important as ViewState runs after Init and requires the same controls initalised the same way as the way they were previously rendered. You can load the control using the code from #Mitchel i.e.
Control ControlInstance = LoadControl("MyControl.ascx");
myPlaceholder.Controls.Add(ControlInstance);
Page_Load – Load the content of the controls in here as you would with any control that isn’t dynamically loaded. If you have kept a reference to them in your page_init they will therefore be available here.
Keeping to this structure I haven’t had too much difficulty as this is appears to be the way that ASP.NET was designed to work, even if all the samples on MSDN don’t do it this way. The biggest thing that you then have to watch is tracking what state the page was in in regard to the controls that you have had rendered.
In my case it was take the section number of the multipage survey and reload the questions from the database, so all I had to do was track the currently rendered section number which wasn’t difficult.
Having said all that if you are using dynamic controls just to show and hide different views of the same screen then I suggest you don’t use them. In this case I would much rather use either user controls (with the inappropriate ones hidden), placeholders to mark areas that aren’t to be rendered yet, or separate pages/views etc. as that way you keep the pages to a single responsibility which makes it easier to debug and/or get useful information from the user about which page they were on.
The Microsoft article is very good, but the best article that I have been read is in the bellow link:
https://web.archive.org/web/20210330142645/http://www.4guysfromrolla.com/articles/092904-1.aspx
If you are really interested in ASP.NET Web Forms dynamic controls, I recommend that you study the DotNetNuke CMS Portal. DotNetNuke is one of the best cases using dynamic controls as your core feature to build dynamic portals and pages using ASP.NET Portals. It is free for download in www.dotnetnuke.com.
I hope it helps
I have a lot of web controls created dynamically in an ASP.NET page. In certain postback scenarios (when I click a LinkButton), I want to skip the loading of the old tree of web controls and immediately generate the new tree. Unfortunately, when I generate the new tree, the viewstate of the old tree is loaded into it.
I want to be able to disable the viewstate loading process for this specific scenario, but after the new tree is loaded, the viewstate should work normally.
I've already solved part of the problem, by overriding the LoadViewState method of the web controls, but, unfortunately, this disables the viewstate specific for the control, not for his children too (textboxes, buttons etc.).
Is it possible?
Thank you.
You can try creating the new tree after the viewstate has been loaded, e.g. in Page_Load.
You can do this programatically by clearing the contents of the tree before binding the new data to it. It should be somehting along the lines of
tree.nodes.clear()
and then you just go on as if the tree is empty and add you data by either bounding it or by adding the nodes programatically with
tree.Nodes.Add(New TreeNode("name of node"))
now for regular controls which I gather you are using you will run into a whole host of problems if you don't use viewstate. You can remove them from the control tree in a similar fashion using controlname.controls.clear. Now is you problem primarily the appending of these contorls or is it that you don't want to carry the burden of viewstate? I suggest that if it's the latter that you clear your controls programatically and store viewstate server side somewhere so that it can be loaded from memory instead of using your bandwidth.
As for dynamical loading of controls you will need to ensure tha tyour new controls carry different ID's for them. What I've done in one of my apps is actually keep the old controls, make them invisible and clear the viewstate then on the next call I actually remove them from the tree.
I have answered it here:
How to load a page with its default values after a postback
For example,
If I have a textbox with runat=server on a page, The value will be posted back to the server so I can access the properties in the code-behind.
However, under the following situations, does it still hold true?
A textbox with runat=server but does not appear in the function that is post back to. For example, a button is also on the page, when clicked a post back occurs and within the method that is raised, this textbox was not used.
Within a MasterPage, will a textbox residing on the Masterpage itself be posted back?
Because just thinking, isn't this mechanism bloated in nature?
If all input controls and its value are posted back on every single button click (even when the input control is not needed), doesn't this deteriorate performance?
Having just one Form Tag on the page really restricts us to using this mechanism?
Truly Understanding ViewState is a must read article on the subject of ASP.NET ViewState
There are several options to cut down on the bloat (and yes, there's a lot of it when dealing with lots of controls):
Use AJAX to post only the items required - although be careful to allow clients that don't have JavaScript enabled to still use the page/ site.
The MVC framework allows multiple form tags to be used so you can group sections if needs be.
Set the EnableViewState to false on pages/ controls.
Break up your pages into smaller ones.
Additionally, check out this brilliant graphical representation of the Page Life Cycle in ASP.NET.
Every input on the page is posted back fully unless you use ajax, because of the single form tag. Welcome to asp.net...
As long as the method that you're hitting on the server-side is a non-static member of the page's class, it'll have access to the textbox and all other controls on the page.
And yes, all controls rendered to the browser (whether in the MasterPage, user control, etc.) will be available on post-back.
You may want to look into Understanding ASP.NET View State.
There surely are performance hits with this architecture, but (depending on complexity of the page) it's usually not an issue from the server load perspective, because hardware upgrades are typically cheaper than additional programming hours spend on optimizing application performance.
With that said, (and as others have pointed out) look into using AJAX if you want to avoid whole page-level postbacks to the server.
Yes, it's all posted back, and yes it can cause bloat. I'm sure if you search for ViewState you will find plenty of people ranting about it and how to minimise it :)
Yes your text box will be available in both cases, yes it is bloated. This is where AJAX comes into play. Using AJAX you can send just the data you need.
If you want to send a minimal ammount of data, you could use a Page Method (static method on page decorated so the script manager builds javascript to call it or you could call it using jquery or other methods), or a script enabled web service works nice as well.
You also have viewstate which can get very large. ASP.Net MVC is a new paradigm instead of using WebForms which doesn't have view state, or post backs. It embraces HTTP instead of hidding it giving developers more control.
The textbox data would be posted back as noted. In addition to using Ajax, disabling view state greatly imporoves your page's performance though even then data in properties critical to the functioning of controls (Control state) would still be posted back.
If you didn't have postback for every control on the form, you wouldn't be able to access it in code-behind. I.e. if in your button press you wanted to modify the property of the text control you couldn't do that because ASP.Net would know nothing about the text control.
Since the communication between the server and the client is stateless and every time a page is server the server forgets all about it Postbacks are important if you want to work with the same page again. No matter what programming language you use, this or similar mechanism exists for processing server side code.
If you wish to minimize postback (viewstate size), do this.
Set enableviewstate=false on all controls that you don't want posted back.
Use AJAX and web services wherever possible (and don't use UpdatePanel).
Use HTML control as much as possible instead of ASP.Net controls.
Hmm.. There are some excellent suggestion in other answers and good links too.
You've pretty much hit the nail on the head with vanilla ASP.NET - it's not very good! In both the instances you describe the answer is yes - the textbox will be sent with the form.
The whole postback/ViewState problem is a bit of a pain, one of the first things any competent ASP.NET developer learns to do is avoid them!
Is it possible to save ViewState information, e.g. to session, so that when you navigate away from the page it is persisted somehow? Then when you return to that page you can reload the view state and the choices you've made are preserved.
Background
I have two pages, a report page, where you can pick data, do some filtering and sorting etc. and a chart-page, where the data you picked from the report page can be presented in different ways, with different choices for presentation.
If the user has tested different presentations, simply using the back-button could mean quite a few clicks before the user's back at the report page. I'd like a direct link to the report page.
Using QueryString to save control states is not an option.
I can't customize the ViewState storage for the whole application.
Yes, it's possible to store the Viewstate in something like a database. You just need to implement one of the viewstate providers. See here for an example using the SqlViewStateProvider.
Edit: Just re-read your post, and saw that you said you couldn't customize how the viewstate is stored for the whole application. If that's the case, you might want to look into storing it in a session. Scott Hanselman discusses that here.
Your link could automatically navigate back the required number of pages using JavaScript. Look at window.history, if you can count the number of pages forward you can navigate back that many.
The ViewState is already designed to persist the state of the user controls. If your user has made a selection and that selection is processed server side with a full page postback the new state of the controls will be saved in the ViewState (hidden input __VIEWSTATE).
If your report is using AJAX and partial page postbacks then you won't get the ViewState on the page anyway.
Just to clarify, the SQLViewstateProvider is NOT an application wide implementation. You have to create a class which inherits from the System.Web.UI.Page object and overrides the Save And Load viewstate methods of the Parent Page class. For each page that you want the viewstate to be saved on server side you then have to inherit from your newly created Page Template (Which in turn inherits and overrides the System.WEb.UI.Page class).
So it is applied on a per-page basis, not on an application-wide basis.
HEADS UP: Some controls might contain some client-side javascript code which may reference the viewstate on client-side (duh). If the viewstate is now stored on server-side you will get a null-reference exception (for instance, clicking a commandfield in a gridview). I'm working on a workaround for this problem, but unfortunately I do not have any concrete solution as of yet.
This is a bad idea, just use querystrings. I would be interested to know why they are not an option.
I've got a web application that I'm trying to optimize. Some of the controls are hidden in dialog-style DIVs. So, I'd like to have them load in via AJAX only when the user wants to see them. This is fine for controls that are mostly literal-based (various menus and widgets), but when I have what I call "dirty" controls - ones that write extensive information to the ViewState, put tons of CSS or script on the page, require lots of references, etc - these are seemingly impossible to move "out of page", especially considering how ASP.NET will react on postback.
I was considering some kind of step where I override Render, find markers for the bits I want to move out and put AJAX placeholders in there, but not only does the server overhead seem extreme, it also feels like a complete hack. Besides, the key element here is the dialog boxes that contain forms with validation controls on them, and I can't imagine how I would move the controls and their required scripts.
In my fevered imagination, I want to do this:
AJAXifier.AJAXify(ctlEditForm);
Sadly, I know this is a dream.
How close can I really get to a quick-and-easy AJAXification without causing too much load on the server?
Check out the RadAjax control from Telerik - it allows you to avoid using UpdatePanels, and limit the amount of info passed back and forth between server and client by declaring direct relationships between calling controls, and controls that should be "Ajaxified" when the calling controls submit postbacks.
I recommend that you walk over to your local book store this weekend, get a cup of coffee and find jQuery in Action by Manning Press. Go ahead and read the first chapter of this 300 page book in the store, then buy it if it resonates with you.
I think you'll be surprized by how easy jQuery lets you perform what your describing here. From ajax calls to the server in the background, to showing and hiding div tags based on the visitor's actions. The amount of code you have to write is super small.
There are a bunch of good JavaScript libraries, this is just one of them that I like, and it really is easy to get started. Start by including a reference to the current jQuery file with a tag and then write a few lines of code to interact with your page.
Step one is to make your "dirty" pieces self contained user controls
Step two is to embed those controls on your consuming page
Step three is to wrap each user control tag in their own Asp:UpdatePanel
Step four is to ensure your control gets the data it needs by having it read from properties which check against the viewstate for pre-existing values. I know this makes your code rely on ugly global variables but it's a fast way to get this done.
Your mileage may vary.