Skip to main content

🐞 Fixing AjaxControlTookit Unexpected Page Reload in ASP.NET Web Form Application

·906 words·5 mins
Bemn
Author
Bemn
Hong Konger.

😨 Problem #

TL;DR: see Summary and the working code snippet if you want to try it right away.

In my company there is an old Web Form Website running .NET Framework 3.5. I migrated it to an ASP.NET Web Form Application running .NET Framework 4.7.2 few months ago. The website is running just like the old one but recently a user reported that:

a <select> list which should triggers an AJAX call and refresh part of the page does not work. Instead, the page is reloaded everytime when the item in the <select> list is changed.

I used almost a day to figure out what’s (probably) happened and I hope that this article will be helpful to someone in the future who (still) encounter this problem.

Expected vs Actual Behavior #

There is a <form> containing a <select> list (generated by <asp:DropDownList>). When the selected option is changed, an AJAX call will be fired and new items will be fetched. Besides, there is a text area for user to input remarks. A submit button will submit the form.

image-20210212150215986

Expected #

The page will not be reloaded when the selected option is changed. The content in the text area retains.

Actual #

Once the selected option is changed, a form POST is fired (instead of an AJAX call). The content in the text area is therefore being cleaned.

This is related to AJAX calls so the first thing I checked was how the AJAX call was initiated.

😀 Solution (Part 1): Updating AjaxControlToolkit #

In the .aspx file I saw a <asp:UpdatePanel> and a <asp:DropDownList>. This is the simplified structure of that part:

<asp:UpdatePanel runat="server" ID="UpdatePanel2" UpdateMode="Conditional" ChildrenAsTriggers="true">
  <ContentTemplate>
    <asp:DropDownList runat="server" ID="ddItemTypes" AutoPostBack="true" OnSelectedIndexChanged="ddItemTypes_OnSelectedIndexChanged" />
  </ContentTemplate>            
</asp:UpdatePanel>

AutoPostBack is there so the app should use AjaxControlToolkit to make AJAX calls (I guess that’s the standard right?).

Nuget Package Update #

I checked the package.config:

<package id="AjaxControlToolkit" version="4.1.50508" targetFramework="net35" />

Looks like the targetFramework is not right. Maybe it’s time to update the package too.

<package id="AjaxControlToolkit" version="20.1.0" targetFramework="net472" />

Some Breaking Changes #

AjaxControlToolkit has some breaking changes since version 15+. In short, you need to:

  1. Uninstall the old version and install the new version of AjaxControlToolkit.

  2. Change <asp:ToolkitScriptManager> to <asp:ScriptManager>

  3. Remove some unused configs in web.config (see this)

  4. [Optional] Install AjaxControlToolkit.HtmlEditor.Sanitizer

    • Change the namespace AjaxControlToolkit.HTMLEditor to AjaxControlToolkit.HtmlEditor

    • Change the namespace AjaxControlToolkit.HTMLEditor.ToolbarButton to AjaxControlToolkit.HtmlEditor.ToolbarButtons

You may also read the official complete migration guide.

After I upgrade the Nuget package, the AJAX calls was working partially: It only works on Chromium-base browsers like Chrome and Edge. However, it does not work on Firefox (85.0) and Safari (14.0.1).

😐 😐 😐

Different __doPostBack() in Firefox? #

This is what I got from Firefox’s dev tool:

call stack form Firefox image-20210212140706748

The initiator is from __doPostBack() in the .aspx page. In Chrome, it’s from ScriptResource.axd. Below is the expected call stack: the call is an XHR request fired from ScriptResource.axd.

Expected call stack image-20210212141300293

Looks like the stack items from bottom to _doPostBack() are the same. I therefore dug into _doPostBack() and eventually found this piece of code in ScriptResource.axd:

if (!this._postBackSettings.async) {            
    form.onsubmit = this._onsubmit;            
    this._originalDoPostBack(eventTarget, eventArgument);            
    form.onsubmit = null;            
    return;        
}

if this._postBackSettings.async is false, _originaldoPostBack() will be called and that is the __doPostBack() function defined in the .aspx page. This will trigger a page reload instead of an AJAX call.

Furthermore, I found that the asyncTarget in _postBackSettings is null but in Chrome that target is the <select> list.

In Firefox (v85.0) the javascript event initiated is somehow keep propagating to a higher level than the <select> list (probably to the form’s level) and therefore being treated as a form POST instead of an AJAX call.

😀 Solution (Part 2): Adding <asp:AsyncPostBackTrigger> #

I did some research related to the Firefox-specific page reload and seems none of them were talking about the issue I met. I tried to study the fundamentals of this PostBack behaviour.

Finally I got this article Avoid (Prevent) Page refresh (PostBack) after SelectedIndexChanged is fired in ASP.Net DropDownList. It suggested what I might missing is an asp:AsyncPostBackTrigger.

Also, this StackOverflow answer menitoned the asp:AsyncPostBackTrigger thing.

So my understanding is:

When using the ScriptManager (i.e. the one we introduced when updating the AjaxControlToolkit), we need to define an asp:AsyncPostBackTrigger Trigger in order to make the call AJAX.

Therefore, I added a Trigger section to the .aspx file:

<Triggers>
  <asp:AsyncPostBackTrigger ControlID="ddItemTypes" EventName="SelectedIndexChanged" />
</Triggers>

This Trigger section should be placed under the same <asp:UpdatePanel> with the <asp:DropDownList>. Here is the complete snippet:

The Working Code Snippet #

<asp:UpdatePanel runat="server" ID="UpdatePanel2" UpdateMode="Conditional" ChildrenAsTriggers="true">
  <ContentTemplate>
    <asp:DropDownList runat="server" ID="ddItemTypes" AutoPostBack="true" OnSelectedIndexChanged="ddItemTypes_OnSelectedIndexChanged" />
  </ContentTemplate>
  <Triggers>
    <asp:AsyncPostBackTrigger ControlID="ddItemTypes" EventName="SelectedIndexChanged" />
  </Triggers>
</asp:UpdatePanel>

Note:

  1. EventName is optional here but since I just want this trigger to listen the SelectedIndexChanged event, I add them all.

  2. The ControlID has to be an exact match with the ID of <asp:DropDownList>.

After I added the <asp:AsyncPostBackTrigger>, all the browsers including Chrome, Edge, Firefox (85.0) and Safari (14.0.1) are working.

Summary #

When your .NET 4.5+ ASP.NET Web Form Application using AjaxControlToolkit does not work as expected and cause the controls like <asp:DropDownList> cannot fire an AJAX call (but triggers a form POST), make sure that:

  1. The AjaxControlToolkit Nuget package is up to date (and did the proper migration steps here).

  2. The <asp:UpdatePanel> section should contain a <Triggers> section. In that section there is an <asp:AsyncPostBackTrigger> with ControlID same as the ID of your control (which is also placed under the same UpdatePanel section).

Related

🐞 Battling With Gulp and Node
·926 words·5 mins
Tackling the generation gap between old Gulp and young Node when building my website.
🐞 GitVersioning: 'ThisAssembly' Is Inaccessible Due to Its Protection Level
·202 words·1 min
I can’t use ‘ThisAssembly’ in my .NET MVC project. Here’s why.
Set Up Cloudflare for AWS Route 53 Domains
·793 words·4 mins
This article shows how to configure Cloudflare for an existing website using AWS Route 53.