I'm trying to fully understand the WebForm event model (not the page lifecycle, but how the events are wired up when specified declaratively in the .aspx or .ascx files.
Take the Button control for example. It has a Click event that you can wire to in the code-behind, but it has an "OnClick" event in the .aspx/.ascx file.
I used the .NET Reflector and the Button control has a PROTECTED OnClick method, but that shouldn't be available to be assigned by the .aspx/.ascx. Unless I'm missing something.
Does anyone know why the "On" prefix is added?
Just to clarify a bit: I understand the naming convention works. I'd like to know how the "OnClick" in the .aspx/.ascx gets translated into .Click += new EventHandler(blahName); I.e. if I create a ControlChanged EventHandler, do I need to do anything special to get the OnControlChanged to show up validly in the .aspx/.ascx file?
Those store references to the delegates that the calling code will be wiring up using events; in order to distinguish between the event itself, and the delegate.
It's more than a naming convention because events in user controls automatically get the "On" prefix in the declarative syntax.
For example, I have a UserControl that declares a ProjectSelected event. To add a handler declaratively, I set the OnProjectSelected attribute.
UserControl:
public event EventHandler<ProjectSelectedEventArgs> ProjectSelected;
Adding handler declaratively:
<user:ProjectList id="uxProjectList" runat="server"
OnProjectSelected="uxProjectList_ProjectSelected" />
Adding handler in code behind:
uxProjectList.ProjectSelected += uxProjectList_ProjectSelected;
This confused the hell out of me twice, once when I couldn't figure out why the event wasn't available declaratively, and again when I named the event "OnProjectSelected" and the attribute became "OnOnProjectSelected".
It's just a naming convention used when raising events. OnSomethingHappened ... OnClick, OnChange, OnClose. I don't think there is anything magical or sinister, it's just a convention.
Semantically it is basically an old throwback to VB traditions where event listeners were generally called OnWhatever. Old habits die hard.
Related
I have a custom class (ServerSideValidator.vb) that validates user input on server side (it doesn't use any of the .NET built in validators, therefore Page.Validate() is not an option for me). I am calling the Validate() method on page.IsPostback event and the class performs without any problem
My issue is, when validation fails (returns false), I want to stop the postback event handler from firing, but load the page along with all the controls and user-input values in them. If I do, Response.End(), the page comes up blank. I can programmatically instruct the page to go to the previous page (original form before postback), but it loses all user-inputs.
I thought of creating a global boolean variable in the page code behind file and check the value before performing any postback method, but this approach takes away from my plan to provide all functionalities inside the class itself. The page object is being referenced to ServerSideValidator.
Seems like all the postback related properties/variables I come across inside Page class are 'Readonly' and I can't assign value(s) to control/prevent postback event from firing.
Any idiea on how I can accomplish this? Please let me know if you need further details
It's probably easier to post back to the same page every time and do your validation there, specifically on the page load event. If the validation fails, you're already on the correct page and don't have to go to a previous page. If the validation succeeds, then you can redirect to another page if you wish, in which case you probably don't need any data.
Edit: This isn't exactly what you asked for, but I have a feeling it will do what you want while fitting into the existing ASP.NET validation design. See http://www.dotnetcurry.com/ShowArticle.aspx?ID=197 and https://web.archive.org/web/20211020145934/https://www.4guysfromrolla.com/articles/073102-1.aspx
Basically, you create a custom class just like you have now, but inherit from BaseValidator. To follow your design, you can create an enum called ValidationType which has Alphabetic, Alphanumeric, etc. In your custom class, create a property called ValidationType that uses the ValidationType enum. Of course you have to add all the validation logic. Then in your aspx page, you can add your custom validator to the page and set ValidationType="Alphabetic", etc. with full IntelliSense support.
Since you use BaseValidator, all the regular validation techniques will work including Page.Validate(), Page.IsValid, etc. You can even create client-side validation JavaScript if you wish.
Having said this, someone has probably already done most of this for you.
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 !
What is the difference between using the OnClick attribute of an ASP.Net Button:
<asp:Button ID="btn" runat="server" Text="New" OnClick="enterFunctionHere" />
vs.
using the event directly in the function:
Sub addNew() Handles btn.Click
Thanks!
UPDATE
If I can do both in VB, which is better? Are they equal?
At the least, in the first option, the generated class for the .aspx page is responsible for wiring up the event handler (and thus, requires the event handler to be Protected); whereas, in the second option, the codebehind class is responsible for wiring up the event handler (so the event handler can be Private).
I'm not familiar with how exactly the Handles keyword is implemented in VB.NET, but it may also affect the timing of the wire-up (I know that wiring up an event in a codebehind's OnInit method will wire up the method at a different time in the page cycle than wiring it up through the markup, and a few obscure cases where that matters).
I, personally, prefer using the Handles method (or using += in C# in the OnInit override). This allows the compiler to verify that the methods exist and don't have to be unnecessarily exposed to inheriting classes. Being compiled also helps when using refactoring tools, looking up usages, etc.
There's no appreciable difference. Both are equivalent to using the AddHandler keyword. Using the OnClick attribute is more compatible with ASP.NET code that might use C#, while using the Handles keyword is more compatible with Windows Forms VB.NET code.
Both add an event handler. Handles is available in VB.Net, not C#.
Also, you can add an event in code with the AddHandler method of a page.
The first one is an example of how pages using c# code behind register the event to the function. This one then needs a method matching the definition.
The second one is the vb.net way of attaching the event to a function.
Is there a difference in behavior between adding a control to the ASPX page directly and loading a control programmatically and adding to a placeholder?
The control inherits from System.Web.UI.WebControls.DataBoundControl.
The reason I ask is that I have a control that works when I add it to the ASPX page like so:
...
<blah:GoogleMap ID="GoogleMap1" runat="server" Width="640px" Height="600px" ... DataSourceID="_odsMarkers" DataAddressField="Address" DataTextField="Description">
</blah:GoogleMap>
...
But not when I use the following in a codebehind page:
GoogleMap map = (GoogleMap)this.LoadControl(typeof(GoogleMap), new object[] { });
//... set properties
this.placeholder1.Controls.Add(map); //add to placeholder
Anyone have any ideas why this might be the case?
The control tree ends up the same if you define in markup or add programmatically. However there is plenty of room for the control implementor to screw up along the way.
You can go look how ASP.NET compiles the aspx:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files
The timing when the control is added to the page might be an issue. The usual pattern is add the control in an overload of the CreateChildControls method. If the control needs to resolve viewstate you need to make sure this is called during init, e.g. by calling EnsureChildControls.
Adding to ninja's debbugging hint. Does it make any difference if you add a label the same way. Does it show up?
Is this a user control or server control?
If it's a user control they should be loaded by their path and not their type:
GoogleMap map = (GoogleMap)this.LoadControl("~/Controls/GoogleMap.ascx");
If it's server control then you can just new up an instance:
GoogleMap map = new GoogleMap();
after you have the instance and add it to the control tree (by inserting it into the PlaceHolder) it should perform the same as when it would have been declared in the markup.
If you are setting properties outside of the LoadControl call, why are you making that new empty object array instead of just using the overload that has one parameter?
Also, if you attach a debugger to it and step through, do you notice anything weird about the control before you do your Controls.Add() call? Is there an exception being thrown? if so, which one? if not, what does the markup in the browser look like for where the placeholder is?
"Works" is kind of ambiguous, but if you mean, event handlers are never executed, you need to load it in the page onload event.
If the control requires the use of viewstate you must ensure that it is added to the page BEFORE the Page_Load event, otherwise viewstate will not be populated and most likely events and other items will not function properly.
One important difference is that if you create a control dynamically, you will not get, by default, any values from skins set. You must manually call control.ApplyStyleSheetSkin(page): http://msdn.microsoft.com/en-us/library/system.web.ui.control.applystylesheetskin.aspx
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.