开发者

Cancel all events from Page_Load

EDIT: for those who come here with a similar problem, now i 开发者_如何学运维know this was a BAD IDEA.

hi, I have something like this:

bool preventEvents;
protected void Page_Load(object sender, eventargs e)
{
    preventEvents = doSomeValidation();
}

protected void Button1_Click(object sender, EventArgs e)
{
    if (preventEvents) return;  
    // ...
}

protected void Repeater1_DataBound(object sender, EventArgs e)
{
    if (preventEvents) return;  
    // ...
}

The problem is that I have A LOT of events on the page.

Is it possible to just cancel all further events without adding the "if" line on every method?

EDIT:

got some interesting answers (thanks to everyone) but not what i was looking for, maybe i should be more specific:

given some condition, is it possible to skip all events after Page_Load and just jump to the rendering, without manually removing/mapping each event?


The problem is that I have A LOT of events on the page.

Yes, that is a problem. Many events in the same page are bad for performance (it means you're storing a lot of state and doing many http requests). They are bad for maintainability (you have a lot of code in the same class that's all jumbled together). They are bad for testability (asp.net events are notoriously hard to unit test). And they are bad for usability (can't bookmark, don't work with the back button, can lead to double posts).

The solution is to use the Post/Redirect/Get pattern. The downside is that it will mean re-thinking parts of your application design, but in the end you'll have an app that just works better and faster and is easier to maintain.

Be careful choosing to just skip event processing, as is your plan. Odds are your current page state is the result of several events, and not processing events can break the expected state of your page.


You can't "jump ahead" to the rendering because there aren't any conditionals in ProcessRequestMain to allow for it. The only option is to hack the event handlers of the relevant controls.

protected void Page_Load(object sender, EventArgs e) {
    // DON'T DO THIS!! Years from now, some poor soul tasked with
    // debugging your code will tear their hair out, until they
    // discover the unholy magic you have conjured herein. Keep in mind
    // this is the 21st century and this person knows where you live.
    // 
    // Seriously, just use the validation built in to ASP.NET.
    if ("true".Equals(Request.QueryString["disable_events"], StringComparison.OrdinalIgnoreCase)) {
        // disable *all* event handlers on button controls
        foreach (var b in this.GetControlDescendants().OfType<Button>()) {
            var eventList = (EventHandlerList) typeof (Control)
                .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(b, null);
            typeof (EventHandlerList)
                .GetField("head", BindingFlags.Instance | BindingFlags.NonPublic)
                .SetValue(eventList, null);
        }
    }
}

Utility extension method for completeness:

/// <summary>
/// Performs a breadth-first traversal of a control's control tree. Unlike
/// FindControl, this method does not descend into controls that have not
/// called EnsureChildControls yet.
/// </summary>
/// <returns>Enumerable of the visited controls.</returns>
public static IEnumerable<Control> GetControlDescendants(this Control parent) {
    // Don't force execution of EnsureChildControls
    if (!parent.HasControls()) yield break;

    foreach (Control child in parent.Controls) {
        yield return child;
    }
    foreach (Control child in parent.Controls) {
        foreach (var descendant in child.GetControlDescendants()) {
            yield return descendant;
        }
    }
}


You can use the following but it will be almost the same cost

Protected void Page_Load() {
   if (preventEvents) {
      textbox1.TextChanged -= textbox1_TextChanged;
      dropdownlist1.SelectedIndexChanged -= dropdownlist1_SelectedIndexChanged;
      // and so on
   }
}


You can create wrapping delegate over event handler, something like this:

    private EventHandler CreateCancelableEventHandler(EventHandler handler)
    {
        return (sender, e) =>
                   {
                        if (!preventEvents)
                        {
                            handler.Invoke(sender, e);
                        }
                   };
    }

The disadvantage of this solution is that you will need to subscribe to all events in code-behind, not in the markup. Usage of the subscribing would be like this:

button1.OnClick += CreateCancelableEventHandler(Button1_OnClick);


How about setting AutoEventWireup to False in your page directive? ie.

<%@ Page Language="C#" AutoEventWireup="false" Inherits="MyWebApp.EventWireUpFalse" %>

That way only events you explicitly "wire-up" in OnInit will be called. This will give you far more control of what events are raised in the page life-cycle.


Use a custom validator, and then it just falls into your standard invalid check.


I am having the same problem. My current approach is to overwrite the RaisePostBackEvent Method and check a "cancelEvents" flag. RaisePostBackEvent is responsible to route the postback to its originator.

I'm still testing it for bad side effects - I'd appreciate a note if anybody has experience with this idea.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜