locked
AjaxControlToolkit issue in Async WebForms pages RRS feed

  • Question

  • User-1939812703 posted

    There is an issue with AjaxControlToolkit extenders (or any other extenders) in WebForms pages that run Async tasks.

    If your extender is not visible initially, and you run an async task where you make it visible (e.g. if the visibility should be determined based on the data that you read asynchronously), then you will get the following System.ArgumentException:

    Extender control '[ControlID]' is not a registered extender control. Extender controls must be registered using RegisterExtenderControl() before calling RegisterScriptDescriptors().

    After some analysis, here is what causes this.

    1. ExtenderControl base class, which all AjaxControlToolkit extenders subclass, calls ScriptManager.RegisterExtenderControl() in its OnPreRender method. Moreover, ScriptManager will throw an exception if RegisterExtenderControl() is called during any stage other than PreRender.
    2. For controls that are not visible during the PreRender stage, the OnPreRender method will not be called, and hence the extender control will not be registered.
    3. WebForms pages run all async tasks after the PreRender stage, and before the Render stage. So, if you make your extender visible in the async task, then it will not be visible yet in the PreRender stage, and only in the Render stage.
    4. Finally, extender controls call ScriptManager.RegisterScriptDescriptors() during the Render stage, which throws the above exception due to the control not having been registered in the PreRender stage.

    Has anyone found a fix or workaround for this?

    This seems to be a huge limitation in WebForms, where you cannot effectively use both async tasks and extender controls in the same page.

    Below is a sample web page that illustrates this issue.

    <%@ Page Async="true" MasterPageFile="~/Site.Master" Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %>
    <%@ Import Namespace="System.Threading.Tasks" %>
    <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>
    
    <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
        <asp:Panel ID="Panel1" runat="server">
            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
            <ajaxToolkit:CalendarExtender ID="CalendarExtender1" runat="server" TargetControlID="TextBox1" />
        </asp:Panel>
    </asp:Content>
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            Panel1.Visible = false;
            RegisterAsyncTask(new PageAsyncTask(ReadAsync));
        }
    
        private async Task ReadAsync()
        {
            // Making the panel visible below will result in the following exception:
            // Extender control 'CalendarExtender1' is not a registered extender control.
            // Extender controls must be registered using RegisterExtenderControl() before calling RegisterScriptDescriptors().
            Panel1.Visible = true;
            await Task.CompletedTask;
        }
    </script>


    Sunday, February 9, 2020 11:45 PM

Answers

  • User-719153870 posted

    Hi xomega,

    Please check this doc ASP.NET Page Life Cycle Overview first and you shall understand the load event is before the prerender which is before the render event.

    In your sample code, Panel1.Visible = false; is in the page load event and so that the ScriptManager.RegisterExtenderControl() can not register the CalendarExtender1. You can see the blue way in above pic.

    The appropriate way should be like the purple one in above pic which make it invisible after ScriptManager.RegisterExtenderControl() then make it visible in the async task.

    Best Regard,

    Yang Shen

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 10, 2020 6:01 AM

All replies

  • User-719153870 posted

    Hi xomega,

    Please check this doc ASP.NET Page Life Cycle Overview first and you shall understand the load event is before the prerender which is before the render event.

    In your sample code, Panel1.Visible = false; is in the page load event and so that the ScriptManager.RegisterExtenderControl() can not register the CalendarExtender1. You can see the blue way in above pic.

    The appropriate way should be like the purple one in above pic which make it invisible after ScriptManager.RegisterExtenderControl() then make it visible in the async task.

    Best Regard,

    Yang Shen

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 10, 2020 6:01 AM
  • User-1939812703 posted

    Yang Shen,

    Thanks for your suggestion.

    The example I posted was pretty simplistic just to illustrate the issue. In the real case though, we have a framework that would make some views visible based on a button click or any other event. It works fine with extenders in the non-async case, since those events all happen before PreRender, and the extender controls would be registered during OnPreRender.

    Ideally, we want the framework to support both sync and async pages, and setting Visible = false during OnPreRender would not work for the sync case, since it would override any sync event that ran before OnPreRender and set Visible = true. I guess we'd just have to do it differently for sync and async cases, or come up with some framework to make it work for both cases.

    Monday, February 10, 2020 4:45 PM