July 2013 Release of the Ajax Control Toolkit

I’m super excited to announce the July 2013 release of the Ajax Control Toolkit. You can download the new version of the Ajax Control Toolkit from CodePlex (http://ajaxControlToolkit.CodePlex.com) or install the Ajax Control Toolkit from NuGet:

clip_image002

With this release, we have completely rewritten the way the Ajax Control Toolkit combines, minifies, gzips, and caches JavaScript files. The goal of this release was to improve the performance of the Ajax Control Toolkit and make it easier to create custom Ajax Control Toolkit controls.

Improving Ajax Control Toolkit Performance

Previous releases of the Ajax Control Toolkit optimized performance for a single page but not multiple pages. When you visited each page in an app, the Ajax Control Toolkit would combine all of the JavaScript files required by the controls in the page into a new JavaScript file. So, even if every page in your app used the exact same controls, visitors would need to download a new combined Ajax Control Toolkit JavaScript file for each page visited.

Downloading new scripts for each page that you visit does not lead to good performance. In general, you want to make as few requests for JavaScript files as possible and take maximum advantage of caching.

For most apps, you would get much better performance if you could specify all of the Ajax Control Toolkit controls that you need for your entire app and create a single JavaScript file which could be used across your entire app. What a great idea!

Introducing Control Bundles

With this release of the Ajax Control Toolkit, we introduce the concept of Control Bundles. You define a Control Bundle to indicate the set of Ajax Control Toolkit controls that you want to use in your app.

You define Control Bundles in a file located in the root of your application named AjaxControlToolkit.config. For example, the following AjaxControlToolkit.config file defines two Control Bundles:

<ajaxControlToolkit>
  <controlBundles>
    <controlBundle>
      <control name="CalendarExtender" />
      <control name="ComboBox" />
    </controlBundle>
    <controlBundle name="CalendarBundle">
      <control name="CalendarExtender"></control>
    </controlBundle>
  </controlBundles>
</ajaxControlToolkit>

The first Control Bundle in the file above does not have a name. When a Control Bundle does not have a name then it becomes the default Control Bundle for your entire application. The default Control Bundle is used by the ToolkitScriptManager by default. For example, the default Control Bundle is used when you declare the ToolkitScriptManager like this: 

<ajaxToolkit:ToolkitScriptManager runat=”server” />

The default Control Bundle defined in the file above includes all of the scripts required for the CalendarExtender and ComboBox controls. All of the scripts required for both of these controls are combined, minified, gzipped, and cached automatically.

The AjaxControlToolkit.config file above also defines a second Control Bundle with the name CalendarBundle. Here’s how you would use the CalendarBundle with the ToolkitScriptManager:

<ajaxToolkit:ToolkitScriptManager runat="server">

<ControlBundles>

   <ajaxToolkit:ControlBundle Name="CalendarBundle" />

</ControlBundles>

</ajaxToolkit:ToolkitScriptManager>


In this case, only the JavaScript files required by the CalendarExtender control, and not the ComboBox, would be downloaded because the CalendarBundle lists only the CalendarExtender control. You can use multiple named control bundles with the ToolkitScriptManager and you will get all of the scripts from both bundles.

Support for ControlBundles is a new feature of the ToolkitScriptManager that we introduced with this release. We extended the ToolkitScriptManager to support the Control Bundles that you can define in the AjaxControlToolkit.config file.

Let me be explicit about the rules for Control Bundles:

1. If you do not create an AjaxControlToolkit.config file then the ToolkitScriptManager will download all of the JavaScript files required for all of the controls in the Ajax Control Toolkit. This is the easy but low performance option.

2. If you create an AjaxControlToolkit.config file and create a ControlBundle without a name then the ToolkitScriptManager uses that Control Bundle by default. For example, if you plan to use only the CalendarExtender and ComboBox controls in your application then you should create a default bundle that lists only these two controls.

3. If you create an AjaxControlToolkit.config file and create one or more named Control Bundles then you can use these named Control Bundles with the ToolkitScriptManager. For example, you might want to use different subsets of the Ajax Control Toolkit controls in different sections of your app.

I should also mention that you can use the AjaxControlToolkit.config file with custom Ajax Control Toolkit controls – new controls that you write. For example, here is how you would register a set of custom controls from an assembly named MyAssembly:

<ajaxControlToolkit>

<controlBundles>

   <controlBundle name="CustomBundle">

      <control name="MyAssembly.MyControl1" assembly="MyAssembly" />

      <control name="MyAssembly.MyControl2" assembly="MyAssembly" />

   </controlBundle>

</ajaxControlToolkit>


What about ASP.NET Bundling and Minification?

The idea of Control Bundles is similar to the idea of Script Bundles used in ASP.NET Bundling and Minification. You might be wondering why we didn’t simply use Script Bundles with the Ajax Control Toolkit. There were several reasons.

First, ASP.NET Bundling does not work with scripts embedded in an assembly. Because all of the scripts used by the Ajax Control Toolkit are embedded in the AjaxControlToolkit.dll assembly, ASP.NET Bundling was not an option.

Second, Web Forms developers typically think at the level of controls and not at the level of individual scripts. We believe that it makes more sense for a Web Forms developer to specify the controls that they need in an app (CalendarExtender, ToggleButton) instead of the individual scripts that they need in an app (the 15 or so scripts required by the CalenderExtender).

Finally, ASP.NET Bundling does not work with older versions of ASP.NET. The Ajax Control Toolkit needs to support ASP.NET 3.5, ASP.NET 4.0, and ASP.NET 4.5. Therefore, using ASP.NET Bundling was not an option.

There is nothing wrong with using Control Bundles and Script Bundles side-by-side. The ASP.NET 4.0 and 4.5 ToolkitScriptManager supports both approaches to bundling scripts.

Using the AjaxControlToolkit.CombineScriptsHandler

Browsers cache JavaScript files by URL. For example, if you request the exact same JavaScript file from two different URLs then the exact same JavaScript file must be downloaded twice. However, if you request the same JavaScript file from the same URL more than once then it only needs to be downloaded once.

With this release of the Ajax Control Toolkit, we have introduced a new HTTP Handler named the AjaxControlToolkit.CombineScriptsHandler. If you register this handler in your web.config file then the Ajax Control Toolkit can cache your JavaScript files for up to one year in the future automatically.

You should register the handler in two places in your web.config file: in the <httpHandlers> section and the <system.webServer> section (don’t forget to register the handler for the AjaxFileUpload while you are there!).

<httpHandlers>
   <add verb="*" path="AjaxFileUploadHandler.axd"
        type="AjaxControlToolkit.AjaxFileUploadHandler, AjaxControlToolkit" />
   <add verb="*" path="CombineScriptsHandler.axd"
        type="AjaxControlToolkit.CombineScriptsHandler, AjaxControlToolkit" />
</httpHandlers>
<system.webServer>
 <validation validateIntegratedModeConfiguration="false" />
 <handlers>
   <add name="AjaxFileUploadHandler" verb="*" path="AjaxFileUploadHandler.axd"
        type="AjaxControlToolkit.AjaxFileUploadHandler, AjaxControlToolkit" />
   <add name="CombineScriptsHandler" verb="*" path="CombineScriptsHandler.axd"
        type="AjaxControlToolkit.CombineScriptsHandler, AjaxControlToolkit" />
 </handlers>
<system.webServer>

The handler is only used in release mode and not in debug mode. You can enable release mode in your web.config file like this:

<compilation debug=”false”>

You also can override the web.config setting with the ToolkitScriptManager like this:

<act:ToolkitScriptManager ScriptMode=”Release” runat=”server”/>

In release mode, scripts are combined, minified, gzipped, and cached with a far future cache header automatically.

When the handler is not registered, scripts are requested from the page that contains the ToolkitScriptManager:

clip_image003

When the handler is registered in the web.config file, scripts are requested from the handler:

clip_image005

If you want the best performance, always register the handler. That way, the Ajax Control Toolkit can cache the bundled scripts across page requests with a far future cache header. If you don’t register the handler then a new JavaScript file must be downloaded whenever you travel to a new page.

Dynamic Bundling and Minification

Previous releases of the Ajax Control Toolkit used a Visual Studio build task to minify the JavaScript files used by the Ajax Control Toolkit controls. The disadvantage of this approach to minification is that it made it difficult to create custom Ajax Control Toolkit controls.

Starting with this release of the Ajax Control Toolkit, we support dynamic minification. The JavaScript files in the Ajax Control Toolkit are minified at runtime instead of at build time.

Scripts are minified only when in release mode. You can specify release mode with the web.config file or with the ToolkitScriptManager ScriptMode property.

Because of this change, the Ajax Control Toolkit now depends on the Ajax Minifier. You must include a reference to AjaxMin.dll in your Visual Studio project or you cannot take advantage of runtime minification. If you install the Ajax Control Toolkit from NuGet then AjaxMin.dll is added to your project as a NuGet dependency automatically. If you download the Ajax Control Toolkit from CodePlex then the AjaxMin.dll is included in the download.

This change means that you no longer need to do anything special to create a custom Ajax Control Toolkit. As an open source project, we hope more people will contribute to the Ajax Control Toolkit (Yes, I am looking at you.) We have been working hard on making it much easier to create new custom controls. More on this subject with the next release of the Ajax Control Toolkit.

A Single Visual Studio Solution

We also made substantial changes to the Visual Studio solution and projects used by the Ajax Control Toolkit with this release. This change will matter to you only if you need to work directly with the Ajax Control Toolkit source code.

In previous releases of the Ajax Control Toolkit, we maintained separate solution and project files for ASP.NET 3.5, ASP.NET 4.0, and ASP.NET 4.5. Starting with this release, we now support a single Visual Studio 2012 solution that takes advantage of multi-targeting to build ASP.NET 3.5, ASP.NET 4.0, and ASP.NET 4.5 versions of the toolkit.

This change means that you need Visual Studio 2012 to open the Ajax Control Toolkit project downloaded from CodePlex. For details on how we setup multi-targeting, please see Budi Adiono’s blog post:

http://www.budiadiono.com/2013/07/25/visual-studio-2012-multi-targeting-framework-project/

Summary

You can take advantage of this release of the Ajax Control Toolkit to significantly improve the performance of your website. You need to do two things: 1) You need to create an AjaxControlToolkit.config file which lists the controls used in your app and 2) You need to register the AjaxControlToolkit.CombineScriptsHandler in the web.config file.

We made substantial changes to the Ajax Control Toolkit with this release. We think these changes will result in much better performance for multipage apps and make the process of building custom controls much easier. As always, we look forward to hearing your feedback.

Discussion

  1. Stilgar says:

    Is it bad that I am excited about something about Web Forms?

  2. Chris Butterfield says:

    Just updated to the latest version today, and my Accordions stopped working. They are displayed one the page using the asp ScriptManager, and the top pane is expanded, but I cannot expand any other pane. Reverted back to old version, works fine.

    Also noticed that when I specify an Accordion or an AccordionPane control in the AjaxControlToolkit.config file I get:

    __Offending URL: http://localhost:1364/CSI/ManageAccounts.aspx
    Source: AjaxControlToolkit
    Message: Could not find control ‘Accordion’. Please make sure you entered the correct control name in AjaxControlToolkit.config file.
    Inner Exception:
    Stack trace: at AjaxControlToolkit.ToolkitScriptManagerConfig.GetControlTypesInBundles(HttpContextBase context, String[] bundles) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManagerConfig.cs:line 146 at AjaxControlToolkit.ToolkitScriptManagerCombiner.GetScriptReferences(HttpContextBase context, String[] bundles) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManagerCombiner.cs:line 207 at AjaxControlToolkit.ToolkitScriptManager.OnLoad(EventArgs e) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManager.cs:line 236 at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
    __

    And:
    Offending URL: http://localhost:1364/CSI/ManageAccounts.aspx
    Source: AjaxControlToolkit
    Message: Could not find control ‘AccordionPane’. Please make sure you entered the correct control name in AjaxControlToolkit.config file.
    Inner Exception:
    Stack trace: at AjaxControlToolkit.ToolkitScriptManagerConfig.GetControlTypesInBundles(HttpContextBase context, String[] bundles) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManagerConfig.cs:line 146 at AjaxControlToolkit.ToolkitScriptManagerCombiner.GetScriptReferences(HttpContextBase context, String[] bundles) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManagerCombiner.cs:line 207 at AjaxControlToolkit.ToolkitScriptManager.OnLoad(EventArgs e) in f:\TeamCity\buildAgent\work\80acd78aa4c25314\Server\AjaxControlToolkit\ToolkitScriptManager\ToolkitScriptManager.cs:line 236 at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

    Here’s my ControlBundle:

    Logged issue https://ajaxcontroltoolkit.codeplex.com/workitem/27477

  3. @chris — thanks for the detailed report. I want to verify that you are using the ToolkitScriptManager instead of the ScriptManager (all of the control bundle magic happens in the ToolkitScriptManager)

    • Chris Butterfield says:

      Yes, I tried using the standard ASP script manager:

      asp:ScriptManager ID=”scriptManager” runat=”server”

      And then I tried changing to the ToolKitScriptManager with the default bundle :

      ajaxToolkit:ToolkitScriptManager ID=”ToolkitScriptManagerManageAccount” runat=”server”

      as well as trying it with my bundle name set, which is where I ran into the exceptions above when I added the Accordion and AccordionPane to the bundle controls. So far everything else appears to be ok, and the bundles are working for my other pages.

      I did run into one minor issue on a page where I have HtmlEditorExtender defined. In that bundle, I also had to define the AjaxFileUpload control, even though I am not explicitly using it. It appears it is needed by the HtmlEditorExtender, and I am using it’s image upload functionality.

      reverted back to the previous version I was using (I believe it was from October 2011), and the Accordion works with both:

      asp:ScriptManager ID=”scriptManager” runat=”server”
      and
      ajaxToolkit:ToolkitScriptManager ID=”ToolkitScriptManagerManageAccount” runat=”server”

      without the bundle defined, of course.

      I tried to look for ToolkitScriptManagerConfig.cs in the source code repository to see if there is something obvious, but it appears that the last commit on the main branch was back in June, and that file is not in there.

      I was going to work on testing out some more pages on my site with the updated toolkit, but I am out of time for the day, and will start again in the morning.

      Thanks,

      Chris Butterfield

  4. @chris – Also, take a look at the source for the Accordion.aspx page in the Ajax Control Toolkit sample site. The ToolkitScriptManager uses a bundle which looks like this:

    controlBundle name=”Accordion”
    control name=”AccordionExtender”
    control name=”CollapsiblePanelExtender”
    controlBundle

    • Chris Butterfield says:

      Hey, I think that did it! Thanks!

      I was using the control names as defined in the markup in the AjaxControlToolkit.config file, like

      ajaxToolkit:Accordion ID=”MyAccordion” runat=”server” SelectedIndex=”0″ HeaderCssClass=”accordionHeader”
      HeaderSelectedCssClass=”accordionHeaderSelected” ContentCssClass=”accordionContent”
      FadeTransitions=”true” FramesPerSecond=”40″ TransitionDuration=”150″ AutoSize=”None”
      RequireOpenedPane=”true” SuppressHeaderPostbacks=”true”

      and

      ajaxToolkit:AccordionPane ID=”AccordionPane1″ runat=”server”

      which is why I was using “Accordian”, instead of “AccordionExtender”, and “AccordionPane” instead of “CollapsiblePanelExtender”

      Thanks for the tip!

      Chris

  5. Chris Butterfield says:

    But, as the accordion was working previously using the regular ASP script manager, should it not still work in this version? I.e shouldn’t you be able to leave your markup alone and update the toolkit DLLs without having to comb through each page to verify every toolkit extender control?

    Thanks,

    Chris

  6. Chris says:

    in case anybody cares, I had a similar issue with the tab control. You need to bundle these 2:

    for it to work.

    • Chris says:

      the 2 control lines didn’t come through. add these 2 controls:
      TabContainer and AutoCompleteExtender

  7. Jay says:

    I don’t want any controls included, the only thing I am doing is to use the ToolkitScriptManager, so my config file looks like this:

    But then I get an error:

    [NullReferenceException: Object reference not set to an instance of an object.]
    AjaxControlToolkit.ToolkitScriptManagerConfig.GetControlTypesInBundles(HttpContextBase context, String[] bundles) +982
    AjaxControlToolkit.ToolkitScriptManagerCombiner.GetScriptReferences(HttpContextBase context, String[] bundles) +75
    AjaxControlToolkit.ToolkitScriptManagerCombiner.GetCombinedScriptContent(HttpContextBase context, String[] bundles) +37
    AjaxControlToolkit.ToolkitScriptManagerCombiner.GetCombinedScriptContentHash(HttpContextBase context, String[] bundles) +17
    AjaxControlToolkit.ToolkitScriptManager.OnLoad(EventArgs e) +252
    System.Web.UI.Control.LoadRecursive() +54
    System.Web.UI.Control.LoadRecursive() +145
    System.Web.UI.Control.LoadRecursive() +145
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +772

    If I include one control, then the error disappears, eg.

    Why?

    • @jay – this looks like a bug – it should not throw an error in this case. But I want to know why you would want to use the ToolkitScriptManager without using any controls? That seems very strange 🙂

  8. Marco says:

    Hey Stephen,

    Could you please let me know where I can find the solution to open the samples that come with AjaxControlToolkitSampleSite.zip? I only got the folders for every control but not the complete solution.

    Thanks,
    Marco

    • @Marco – the sample site is a Visual Studio Website and not a Web Application Project (WAP). You have to open it as a website from Visual Studio — irritating, yes, but that is the way the Ajax Control Toolkit sample site has always been.

  9. Timoty says:

    Hi Stephen,

    i have a problem with a custom control inherited from HTMLEditor.Editor, without using the AjaxControlToolkit.config.

    There is the full exception stack:

    [Exception: Could not load control MyWebSite.MyHtmlEditor.
    The script reference(s) of this control was not loaded correctly.
    If AjaxControlToolkit.config is used, probably this control is not registered properly.]

    AjaxControlToolkit.ToolkitScriptManager.OnPreRender(EventArgs e) +722
    System.Web.UI.Control.PreRenderRecursiveInternal() +113
    System.Web.UI.Control.PreRenderRecursiveInternal() +222
    System.Web.UI.Control.PreRenderRecursiveInternal() +222
    System.Web.UI.Control.PreRenderRecursiveInternal() +222
    System.Web.UI.Control.PreRenderRecursiveInternal() +222
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4297

    This is the class:

    public class MyHtmlEditor : AjaxControlToolkit.HTMLEditor.Editor, IMyInterface
    {
    protected override void FillTopToolbar()
    {
    // my custom code
    }

    protected override void FillBottomToolbar()
    {
    // my custom code
    }
    }

    There is a way to solve?
    Thanks in advance

  10. My CMS does not let me assign a separate ScriptManager for the 3 pages I use the AjaxControlToolkit on. Adding the ACT-scriptmanager-tag results in error (see link above). I really would like to use the newest version but can’t seem to do so. is there another way to get the ACT-scriptmanager to work to get ACT to work?

    • @Jeroen — according to the error, you have multiple ScriptManagers in a page (a ToolkitScriptManager counts as a ScriptManager). Can you replace your current ScriptManager in your master page with the ToolkitScriptManager? Otherwise, you will need to remove the ScriptManager from your master page and add ToolkitScriptManagers to individual pages.

  11. Larry says:

    Great Work!! I really like the option of having a single Java Script file for the entire application, which in turn will reflect on the system speed.