Where are delegate instances added to events when generating automatic event handlers? - c#

When I double click on a button (myButton) in Design view of a .aspx web form, an event handler is automatically generated in the code behind: protected void myButton_Click(object sender, EventArgs e)
Now if I understand correctly, in order to associate that method with the Button.Click event, somewhere there has to be something like: myButton.Click += new EventHandler(this.myButton_Click);
However, I can't seem to find that anywhere. I've used Ctrl+F for the entire solution and I've checked the mywebform.aspx.designer.cs.
At first I thought it was because the .aspx page's AutoEventWireup was set to true. However, even after making AutoEventWireup false, the button still responds to being clicked by running the code in protected void myButton_Click(object sender, EventArgs e)
I understand that you shouldn't mess with generated code, and I don't intend to, I just want to know more about how this is working under the hood.

The assignment of the event handler is actually done in the asp markup. Here's a link to a bunch of different properties that can be declaratively assigned to your button.
Here's another MSDN link about using the OnClick attribute.
I'm guessing that your ASP markup for the button has the following property assigned:
OnClick="myButton_Click"
As for how it gets translated into an assignment, the page gets compiled at runtime upon the first time it is requested (ASP.NET Compilation Overview).

Related

Making an EventHandler delegate association in (!IsPostBack) doesn't work even on first page load --- why?

Works:
protected void Page_Load(object sender, EventArgs e)
{
myButton.Click += new EventHandler(myButton_Click);
}
Doesn't work:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
myButton.Click += new EventHandler(myButton_Click);
}
}
Now, what I expected in the second example was for the eventhandler to be wired to the button only when it is not a postback (i.e. the first time the page is loaded), and then on any postback, the button would no longer run the method associated with the event. That doesn't seem to be the case --- from the very first load (not a postback), the button does nothing when clicked.
My suspicion is that this is related to the page life cycle --- but I'm not quite sure where this falls into that. If I understand correctly, the method associated with the event gets run after the page has posted back (even if you clicked it on the first time the page loads), but I'm referring to the wiring up of the event to the method with the EventHandler delegate, not the actual running of the associated method.
Note: this is purely an attempt to gain a better understanding of what's going on behind the scenes, and not an attempt to solve a real world problem.
You need to think about how the code is executed with each page request. What happens on the server is that a class instance is created for each request and if the line of code
myButton.Click += new EventHandler(myButton_Click);
is conditional it means the event handler is not wired to the event on postback.
In other words if you write something like
<asp:Button ID="myButton" runat="server" OnClick="myButton_Click" />
This translates to the code you wrote and the event gets wired with each request.
The problem is, the event handler can only trigger when it IS a postback. The wiring of event handlers affects this instance of the class only. When a postback occurs, a new instance of your page is created and the event handler is no longer wired. You need to wire it in order for it to get triggered.
The best thing to do is always wire the event handlers. There's no reason not to.
If it is not a Postback, it means that the user just requested a page (HTTP Get request) with typing the URL in the browser (or clicking a link, or...).
If the user clicked on a button, then the browser makes a HTTP Post request, which on the server side, ASP.NET marks the page that is requested with setting the property IsPostBack to true of the page object.
See the answer on this question.
I guess what you intend to do is disable the button on postback.
You can do that by setting the enabled property to false.
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
myButton.Enabled = false;
}
}

Converting VB.NET Web Form to C# and wiring up events

In My VB.NET web page, I have this standard event. Note the "Handles" clause on teh event declaration.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
In my C# web app, I have this:
protected void Page_Load(object sender, System.EventArgs e)
{
Since C# doesn't have a "Handles" equivalent and from what I've seen, event handlers are wired up using delegate += syntax, I was looking for this, but I could not foind it in the aspx page, aspx.cs file or the aspx.designer.cs file.
In VB, I would have two drop down lists at the top of the Code Editor window and I could select any object on the web form or the web form itself and see the possible events for the object. Selecting the event would either take me to the event handler or if it didn't exists, it would create the stub for me.
I know that the Properties window in C# (and I think in VB, too) has an Event tab that shows the list of events for the selected object GUI object, but "Page" doesn't appear as an object that can be selected.
Where does C# define the hooking up of the event to the handler?
How do I generate a stub for the Page event handler routine? I know that the handle appears by default, but what if it is deleted or I want to add a Page_initialize code? Is there an easy way to get the stub or do I need to go to the Object Browser for the syntax?
In C# web forms, the #Page directive AutoEventWireup property on the markup code behind is defaulted to true, as opposed to false for VB. To see the #Page directive and all of its associated properties, right click on your web page in Solution Explorer and choose 'View Markup'
With AutoEventWireup=true, the runtime will automatically connect the event handlers it finds in your code that match the naming convention form of Page_EventName. You can however turn off this functionality and wire up the page event handlers manually using the standard C# += assignment. If you are using the AutoEventWireup=true, not only must your method name match, but obviously it must also have an appropriate method signature in order to be wired up automatically by the runtime.
See this KB for a good discussion of AutoEventWireup: http://support.microsoft.com/kb/324151
With respect to your second question, in C# there is no way to generate "stubs" for page events like there is in VB. As other have noted, including yourself -- there is similar functionality in C# for generating control object event stubs, via the property window. However, for page events you must know the event name and appropriate signature and code it yourself.
Where does C# define the hooking up of the event to the handler?
Page_Load is a special event that is automatically hooked up. It's a reserved name. So there's nothing you need to do for this event to be hooked up. Just declare it in the code behind.
namespace MyNamespace
{
public class Myclass : System.Web.UI.Page
{
override protected void OnInit(EventArgs e)
{
this.Load += new System.EventHandler(this.Page_Load);
}
private void Page_Load(object sender, EventArgs e)
{
}
}
}
Reference: https://support.microsoft.com/en-us/help/324151/how-to-use-the-autoeventwireup-attribute-in-an-asp-net-web-form-by-usi

Why am I forced to code a rowediting event?

I have a gridview with a button (Template field/LinkButton) that loads another control, but I was getting a runtime error when clicking the button until I added the following:
protected void gvLoans_RowEditing(object sender, GridViewEditEventArgs e)
{
}
Why? I don't want to allow editing. Am I missing something?
You most likely have the event handler assigned in the markup (aspx/ascx). If you remove the event handler assignment you can remove the event handler in the code behind.
Also, make sure you are not enabling the built in editing functionality which could happen if you provide a CommandButton with a CommandName of "Edit".

Calling a function before Page_Load

I have a button that calls function A()
When I click on it I want the calls to be made in that order:
A()
Page_Load()
Right now it's doing:
Page_Load()
A()
Is there a way around that or is it just by design and there's nothing I can do about it?
The easiest way to do this would be to use a HTML Submit button and check to see if it is in the Form on every postback in Page_Init
public void Page_Init(object o, EventArgs e)
{
if(!string.IsNullOrEmpty(Request.Form["MyButtonName"]))
{
A();
}
}
And in your ASP.NET code:
<Button Type="Submit" Name="MyButtonName" Value="Press Here To Do Stuff Early!" />
I think that will work.
Control events (such as the click events of buttons) are called after page_load. The controls are not guarenteed to be fully initialized prior to page_load. If you really need to call a function before page_load has been called based on whether a button has been pressed you'll have to examine the request to check if the button has been pressed (basically old school ASP)
You need to call your function in the Page_Init. Page_Init will happen before Page_Load.
Here's an Overview of the ASP.NET Page Lifecycle.
Not exactly: ASP.NET will always call Page_Load before handling postback events like Button_Click.
However, you can accomplish what you want by redirecting to your page after handling the postback event. (Using the Post-Redirect-Get pattern.)
Inside your Page_Load method, you can avoid running any relevant code twice by checking to see if it's a postback first:
if (!this.IsPostBack) {
// Do something resource-intensive that you only want to do on GETs
}
As Jeff Sternal answered, The Post-Redirect-Get pattern is a good way of solving a problem like this.
In my circumstances i had a calendar and if you clicked a date it would add that to a scheduler. The scheduler would have buttons on each new date that needed to have onclick functions tied to them.
Because the new row was being added with a linkbutton(on the calendar), in the code the new scheduler date was being added at the Postback event handling meaning that the new set of buttons wouldn't have a command tied to them.
The page life Cycle
Post Get Redirect
I don't think it's possible, at least, not in the way described by your question. When you click a button it will send a request to the server which in turn will start processing it, and follow the ASP.NET Page Lifecycle as posted by Joseph.
Alternatively you could try making an AJAX call to a page without reloading the current one you're on and do whatever processing you require.
This is what you want to do for Page Init is called before Page Load.
Take a look at the ASP.net Page Life Cycle
public void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//CALL YOU FUNCTION A()
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
}
}
If your actual goal here is to have your "page loading code" happen after your event handler runs -- for example, if clicking your button changes something in your database, and you want the updated data to be reflected on the page when it loads -- then you could have your "page loading code" get called from a method that gets called later in the ASP.NET page life cycle than your event handler, such as Page_PreRender, instead of calling it from Page_Load.
For example, here's a simplified excerpt from an .aspx.cs page class that has a button event handler that runs before the page population logic, and a confirmation message that is visible on the page only after the button was clicked:
// Runs *before* the button event handler
protected void Page_Load() {
_myConfirmationMessage.Visible = false;
}
// Runs *after* the button event handler
protected void Page_PreRender() {
// (...Code to populate page controls with database data goes here...)
}
// Event handler for an asp:Button on the page
protected void myButton_Click(object sender, EventArgs e) {
// (...Code to update database data goes here...)
_myConfirmationMessage.Visible = true;
}

How do I register event handlers for a web user control in my code behind?

I'm having a problem setting up an event on a form. Here's the setup:
I was given a page with two controls, two different versions of a form for the end-user to fill out- standard and advanced. The two forms have different code and javascript, so the original dev put them in separate web user controls. Aside from the controls is a LinkButton that switches to Advanced mode.
<uc1:Standard runat="server" ID="StandardForm" />
<uc1:Advanced runat="server" ID="AdvancedForm" />
<asp:LinkButton runat="server" ID="lnkAdvanced" Text="Go To Advanced" OnClick="lnkAdvanced_Click" />
lnkAdvanced_Click just takes all the info currently entered to the advanced and flips the Visible.
My problem is that one of the bosses wants the 'Go to Advanced' button inside the standard form, but the .Visible code is on the page. So I thought it could be done using an event, but it doesn't seem to be working.
I tried to set up the event like this:
public event EventHandler AdvanceClick;
protected void lnkAdvanced_Click(object sender, EventArgs e) {
AdvanceClick(sender, e);
}
And when that didn't work, I tried to set up a delegate:
public delegate void AdvancedEventHandler(object sender, EventArgs e);
public event AdvancedEventHandler AdvanceClick;
When I moved the button to the standard form, I expected to be able to type something like:
StandardForm.AdvanceClick += new AdvancedEventHandler(GoToAdvanced);
But it doesn't seem to recognize any events within the control! I get an error: "Standard does not contain a definition for 'AdvanceClick' and no extension method 'AdvanceClick accepting a first argument of type 'Standard' could be found" It finds the other properties just fine, am I going about this the wrong way?
// in your Standard user control
public event EventHandler AdvancedClick;
private void lbtnAdvanced_Click(object sender, EventArgs e)
{
OnAdvancedClick(e);
}
protected void OnAdvancedClick(EventArgs e)
{
if (AdvancedClick != null)
AdvancedClick(this, e);
}
// on your page
StandardForm.AdvancedClick += new EventHandler(StandardForm_AdvancedClick);
private void StandardForm_AdvancedClick(object sender, EventArgs e)
{
// toggle logic here
}
If the standard form has the "Switch to advanced" button. Then, clearly, it has to know about the Advanced form and thus they seem to be pretty tightly coupled. If this is the case, it seems to me that you might as well just have the advanced form as a child of the standard form then... or better yet, merge them into one control.
If you don't like these options you might want to create a third controls which hosts the button and the two forms, along with the logic to move data between them and toggle their visibility.
I personally recommend the single control option. Having tighly coupled controls usually just leads to confusion down the road. You could loosen up the dependency in various ways, but think hard about it before you do so.
In the legacy project I currently work on we have a bunch of examples such as serach forms and search results being split up into multiple controls, but then in the end needing each others instances to function properly. As I said earlier, I wont reccomend this path.
You shouldn't need the delegate because you've created a standard event.
Try in your form load or thereabouts:
StandardForm.AdvanceClick += new EventHandler(GoToAdvanced);
Then somewhere on the page that hosts the 2 user controls
protected void GoToAdvanced(object sender, EventArgs e)
{
//Code that was previously in lnkAdvanced_Click on page.
}
Edit:
It does sound like the setup is wrong.
Can you post the markup for the Host page (at this point we are assuming it is simply the 2 user controls).
Then we are also assuming that the AdvanceClick event is declared in the Standard UC but the error message would indicate that it doesn't.. and the lnkAdvanced_Click method is in the Standard UC?
Then we are assuming the code that is attempting to attach to the custom event is declared in the Host page.
If you could confirm or deny the assumptions i'm sure we could get this cleared up.

Categories

Resources