In ASP.NET, you can bind controls individually (i.e. GridView1.DataBind()) or you can call Page.DataBind() to bind all controls on the page.
Is there any specific difference between the two calls? Are there times when one should be preferred over the other?
Page.DataBind is Control.DataBind. Neither the Page class, nor the TemplateControl class overrides Control.DataBind.
Control.DataBind does little more than call OnDataBinding for the control, then it calls DataBind for each child control.
For choosing between Page.DataBind() versus Control.DataBind(), here is the Microsoft guidance :
"Both methods work similarly. The main
difference is that all data sources
are bound to their server controls
after the Page.DataBind method is
called. No data is rendered to the
control until you explicitly call
either the DataBind method of the Web
server control or until you invoke the
page-level Page.DataBind method.
Typically, Page.DataBind (or DataBind)
is called from the Page_Load event."
There will be cases when you want specify control databinding individually, depending on the current page scenario. For a detailed level of control over which controls are bound and when controls are bound, I opt for the control-level DataBind() methods.
In an ASP.NET page, you can bind directly to public/protected properties of your page's code-behind class. For example:
<form id="form1" runat="server"><%#HtmlUtility.HtmlEncode(MyProperty.ToString())%></form>
In this case, there is no specific control to call .DataBind() on - the page itself is the control. It just so happens that calling Page.DataBind() will also call DataBind() on all child controls, so if you're already doing a Page.DataBind(), there's no need to data bind the controls individually.
This is not a direct answer to subtilities between the two calls, but
about DataBind() vs Page.DataBind() I would like to share an interesting experience which may also really guide you to chose between both :
I just spent one complete day to figure why Ajax calls and events in a huge webapplication were broken (ItemCommand not raised on callbacks and postbacks, lost references, etc).
The reason was I had one ASCX which made a call to Page.DataBind() rather than DataBind() on itself.
It could seem obvious when you found it, but when you are dealing with weird behavior in a >500000 lines application and a lot of complexity in master/pages/controls, it's not.
So beware of Page.DataBind() if you call it at the wrong place !
Related
I have a Report.aspx page that loads different UserControls to view specific object data alternatively. The UserControls inherit the same base type class where override the specific operations methods. I use a specific Service class for each UserControl to get data which implements a specific interface with operations signatures.
I now use a factory pattern to return the actual UserControl type to the Report.aspx and load it into a PanelControl, then call the control method to fetch some data based on some arguments.
This UserControl should be loaded in every postback due to the dynamic nature of it, any other solution is accepted. On every postback though I don't need to load data from the BL which calls the DL. I try to find a solution to show to the BL that I don't need you to call for the data again because I'm just posting back for other issues (e.g. download a report file, print etc.). And I would like this to happen on the BL level, not the ASPX front end. So far I think that I should let BL somehow know this (PostBack or !PostBack). I guess I could just create a parameter true, false and pass the PostBack property.
Any ideas, architecture and best practices are so welcome.
why not wrap the logic to call the BL inside the if(!Page.IsPostback){....} ?
Can you elaborate your statement "On every postback though I don't need to load data from the BL which calls the DL."?? During each postback, user control needs data to show (even if it is same data as last postback) because usercontrol goes through same lifecycle as ASPNET webpage. How can you prevent that?
I have decided that a very nice solution is System.Runtime.Caching in .NET 4.0.
Works very nice for every layer you need to use it.
http://msdn.microsoft.com/en-us/library/dd985642
I have a ScriptControl (requires ScriptManager) with JavaScript to handle client-side interactions and ICallbackEventHandler to communicate back and forth. Everything works perfectly with one or multiple instances of the control on a page. I placed the control inside a GridView with sorting and it still works. However, I place the GridView in an UpdatePanel and now whenever I sort I get the following error for each instance:
Sys.InvalidOperationException: Two components with the same id 'GridView_ctl02_MyControl' can't be added to the application.
Can someone point me in the right direction on how to solve this? I am assuming ScriptManager is not disposing of the old Sys.UI.Control objects before trying to $create() the new ones with the same ID. I thought the UpdatePanel/ScriptManager combination would automatically take care of disposing objects that would be replaced?
Edit: This page appears to support what I thought: http://msdn.microsoft.com/en-us/library/system.web.ui.scriptmanager.registerdispose.aspx
Use the RegisterDispose method to
register dispose scripts for controls
that are inside an UpdatePanel
control. During asynchronous
postbacks, UpdatePanel controls can be
updated, deleted, or created. When a
panel is updated or deleted, any
dispose scripts that are registered
for controls that are inside the
UpdatePanel are called. In typical
page development scenarios, you do not
have to call the RegisterDispose
method.
Just to double check I placed an alert("dispose " + this.element.id) inside my JavaScript dispose() function. Every single instance alerts dispose GridView_ctl02_MyControl, but afterwards I get the error that two components can't have the same name GridView_ctl02_MyControl. I'm at a loss...
When the page unloads, my component's dispose() method is called and Sys.Application.removeComponent() is also called. When the UpdatePanel reloads, only dispose() method is called. For now I have solved this by putting Sys.Application.removeComponent(this); inside the dispose(). I didn't find a shortcut such as $remove (similar to $create), implying you aren't expected to need this often.
This seems logical in that you can keep a component loaded even after its related DOM elements (if any) have been replaced by the UpdatePanel. This way you have more control over the component's life. I can't imagine a use case, but I'm sure you could come up with one.
If I am way off and there is a better approach, please let me know!
While it seems that the "right" way to make a server control is to construct all child controls inside CreateChildControls call. But since it's difficult to know when it will be called (which is the whole point as a perf optimzation), I see most of our devs construct in OnInit, or OnLoad. And this works 99% of the case.
Are there cases where we have to use CreateChildControls?
You should ALWAYS construct your child controls in CreateChildControls. This is the proper time in the Lifecycle to initialize and add them to the control tree. One of the reasons for this is that many times the method EnsureChildContols is called, which then calls CreateChildControls if necessary. Best Practice, just do it.
Read Control Execution Lifecycle
The CreateChildControls method is called whenever the ASP.NET page framework needs to create the controls tree and this method call is not limited to a specific phase in a control's lifecycle. For example, CreateChildControls can be invoked when loading a page, during data binding, or during rendering.
Performance-wise, waiting to create a child control will save your server some unnecessary CPU time. For example, if an exception is raised or the thread is aborted prior to CreateChildControls() being called, the clock cycles necessary to create those controls are saved.
What's your reasoning for saying that creating controls in OnInit is more performant than during CreateChildControls()?
You will get away with creating your controls in Init or Load until you write a control that needs to recreate the controls.
I find it is always best to create the controls in CreateChildControls and then use EnsureChildControls to control ensure they are created when you need them. This allows you the ability to tear down the controls by setting ChildControlsCreated to false and have them recreated again when needed.
I have a usercontrol used in a masterpage.
I have added a page with the masterpage.
Now I need to call a method of the user control from the page.
How to do this? Please help.
Assuming that your method is public and your user control's type is YourUserControlsType, try this :
YourUserControlsType ctrlAtMasterPage =
(YourUserControlsType)Page.Master.FindControl("YouControlsID");
ctrlAtMasterPage.YourPublicMethod();
This should get you your control even if it was added programatically:
In a member of the page you added:
TextBox FoundTextBox = (TextBox)this.Master.FindControl("RunAtServerTextBoxServerID");
If you don't have a member reference to the control you should consider decoupling the page from the control. In an ideal world, the page should not necessarily know about a control it may or may not contain. Therefore, you might look into an implementation of the MVP pattern.
There's a simple implementation of MVP here, and you can see the decoupling in action here. If you reverse the communication from the decoupling example (i.e. page fires an event that the control picks up), then basically, you've decoupled your page from the control. This has a benefit in that if your page changes and the control is no longer in use, the event is not picked up by anything and your page continues to execute with no problems. I find this much more suitable than a potential null reference exception when FindControl doesn't find the control and then you try to execute a method on it.
While decoupling may take a few extra minutes, in many scenarios it turns out to be a worthwhile endeavor.
I'm trying to wrap my head around asp.net. I have a background as a long time php developer, but I'm now facing the task of learning asp.net and I'm having some trouble with it. It might very well be because I'm trying to force the framework into something it is not intended for - so I'd like to learn how to do it "the right way". :-)
My problem is how to add controls to a page programmatically at runtime. As far as I can figure out you need to create the controls at page_init as they otherwise disappears at the next PostBack. But many times I'm facing the problem that I don't know which controls to add in page_init as it is dependent on values from at previous PostBack.
A simple scenario could be a form with a dropdown control added in the designer. The dropdown is set to AutoPostBack. When the PostBack occur I need to render one or more controls denepending on the selected value from the dropdown control and preferably have those controls act as if they had been added by the design (as in "when posted back, behave "properly").
Am I going down the wrong path here?
I agree with the other points made here "If you can get out of creating controls dynamically, then do so..." (by #Jesper Blad Jenson aka) but here is a trick I worked out with dynamically created controls in the past.
The problem becomes chicken and the egg. You need your ViewState to create the control tree and you need your control tree created to get at your ViewState. Well, that's almost correct. There is a way to get at your ViewState values just before the rest of the tree is populated. That is by overriding LoadViewState(...) and SaveViewState(...).
In SaveViewState store the control you wish to create:
protected override object SaveViewState()
{
object[] myState = new object[2];
myState[0] = base.SaveViewState();
myState[1] = controlPickerDropDown.SelectedValue;
return myState
}
When the framework calls your "LoadViewState" override you'll get back the exact object you returned from "SaveViewState":
protected override void LoadViewState(object savedState)
{
object[] myState = (object[])savedState;
// Here is the trick, use the value you saved here to create your control tree.
CreateControlBasedOnDropDownValue(myState[1]);
// Call the base method to ensure everything works correctly.
base.LoadViewState(myState[0]);
}
I've used this successfully to create ASP.Net pages where a DataSet was serialised to the ViewState to store changes to an entire grid of data allowing the user to make multiple edits with PostBacks and finally commit all their changes in a single "Save" operation.
You must add your control inside OnInit event and viewstate will be preserved. Don't use if(ispostback), because controls must be added every time, event in postback!
(De)Serialization of viewstate happens after OnInit and before OnLoad, so your viewstate persistence provider will see dynamically added controls if they are added in OnInit.
But in scenario you're describing, probably multiview or simple hide/show (visible property) will be better solution.
It's because in OnInit event, when you must read dropdown and add new controls, viewstate isn't read (deserialized) yet and you don't know what did user choose! (you can do request.form(), but that feels kinda wrong)
After having wrestled with this problem for at while I have come up with these groundrules which seems to work, but YMMV.
Use declarative controls whenever possible
Use databinding where possible
Understand how ViewState works
The Visibilty property can go a long way
If you must use add controls in an event handler use Aydsman's tip and recreate the controls in an overridden LoadViewState.
TRULY Understanding ViewState is a must-read.
Understanding Dynamic Controls By Example shows some techniques on how to use databinding instead of dynamic controls.
TRULY Understanding Dynamic Controls also clarifies techniques which can be used to avoid dynamic controls.
Hope this helps others with same problems.
If you truly need to use dynamic controls, the following should work:
In OnInit, recreate the exact same control hierarchy that was on the page when the previous request was fulfilled. (If this isn't the initial request, of course)
After OnInit, the framework will load the viewstate from the previous request and all your controls should be in a stable state now.
In OnLoad, remove the controls that are not required and add the necessary ones. You will also have to somehow save the current control tree at this point, to be used in the first step during the following request. You could use a session variable that dictates how the dynamic control tree was created. I even stored the whole Controls collection in the session once (put aside your pitchforks, it was just for a demo).
Re-adding the "stale" controls that you will not need and will be removed at OnLoad anyway seems a bit quirky, but Asp.Net was not really designed with dynamic control creation in mind. If the exact same control hierarchy is not preserved during viewstate loading, all kinds of hard-to find bugs begin lurking in the page, because states of older controls are loaded into newly added ones.
Read up on Asp.Net page life cycle and especially on how the viewstate works and it will become clear.
Edit: This is a very good article about how viewstate behaves and what you should consider while dealing with dynamic controls: <Link>
Well. If you can get out of creating controls dynamicly, then do so - otherwise, what i whould do is to use Page_Load instead of Page_Init, but instead of placing stuff inside the If Not IsPostBack, then set i just directly in the method.
Ah, that's the problem with the leaky abstraction of ASP.NET web forms.
Maybe you'll be interested to look at ASP.NET MVC, which was used for the creation of this stackoverflow.com web site? That should be an easier fit for you, coming from a PHP (thus, pedal-to-the-metal when it comes to HTML and Javascript) background.
I think the answer here is in the MultiView control, so that for example the dropdown switches between different views in the multi-view.
You can probably even data-bind the current view property of the multiview to the value of the dropdown!
The only correct answer was given by Aydsman. LoadViewState is the only place to add dynamic controls where their viewstate values will be restored when recreated and you can access the viewstate in order to determine which controls to add.
I ran across this in the book "Pro ASP.NET 3.5 in C# 2008" under the section Dynamic Control Creation:
If you need to re-create a control multiple times, you should perform the control creation in the Page.Load event handler. This has the additional benefit of allowing you to use view state with your dynamic control. Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the Page.Load event, ASP.NET will apply any view state information that it has after the Page.Load event handler ends. This process is automatic.
I have not tested this, but you might look into it.