AJAX and ASP.NET Template Controls

2 minute read,

I was programming today (err. early morning, 1:00 AM) and stumbled across this error:

[NullReferenceException: Object reference not set to an instance of an object.]

   AjaxControlToolkit.ExtenderControlBase.LoadClientStateValues() in d:\E\AjaxTk-AjaxControlToolkit\Release\AjaxControlToolkit\ExtenderBase\ExtenderControlBase.cs:332

   AjaxControlToolkit.ExtenderControlBase.Page_PreLoad(Object sender, EventArgs e) in d:\E\AjaxTk-AjaxControlToolkit\Release\AjaxControlToolkit\ExtenderBase\ExtenderControlBase.cs:287

   System.EventHandler.Invoke(Object sender, EventArgs e) +0

   System.Web.UI.Page.OnPreLoad(EventArgs e) +86

   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +948

AJAX apparently blew chunks.  After taking a look at the AJAX source code ExtenderControlBase.cs (thank goodness it’s open source), the LoadClientStateValues() was trying to load some hidden client state data stored in a hidden control being resolved by FindControl() method on NamingContainer.  Well, because I was using my AJAX control in a LayoutTemplate, NamingContainer tuned out to be null which was causing ASP.NET’s OnPagePreload -> LoadClientStateValues() to fail.

Here was the offending code:

<asp:Login ID="Login2" runat="server" OnLoginError="Login2_LoginError">
    <LayoutTemplate>
            <fieldset>
                <div class="panel-username">
                    <p>
                        <asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName" CssClass="labeltext bold">Email Address:</asp:Label>

                        <asp:TextBox ID="UserName" runat="server" CssClass="textbox"></asp:TextBox>

                        <ajax:TextBoxWatermarkExtender ID="TextBoxWatermarkExtender1" runat="server" TargetControlID="UserName" WatermarkText="Enter Email Address" WatermarkCssClass="watermark">
                        </ajax:TextBoxWatermarkExtender>

                        <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName" ErrorMessage="User Name is required." ToolTip="User Name is required." ValidationGroup="ctl00$Login2">*</asp:RequiredFieldValidator>

                    </p>
                </div>

                <div class="panel-password">
                    <p>
                        <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password" CssClass="labeltext bold">Password:</asp:Label>

                        <asp:TextBox ID="Password" runat="server" TextMode="Password" CssClass="textbox"></asp:TextBox>

                        <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" ControlToValidate="Password"

                            ErrorMessage="Password is required." ToolTip="Password is required." ValidationGroup="ctl00$Login2">*</asp:RequiredFieldValidator>
                    </p>
                </div>

                <div style="float: left; width: 150px;">
                    <p>
                        <span class="bold">Don't have an account?</span> <a id="A1" href="~/common/SignUp.aspx" runat="server">Register here</a>. It's easy and free!</p>
                </div>

                <div class="panel-button">
                    <p>
                        <asp:ImageButton ID="LoginButton" runat="server" CommandName="Login" ValidationGroup="ctl00$Login2" ImageUrl="~/Images/TransparentPixel.gif" CssClass="cmdSignIn" />
                    </p>

                    <p style="padding-top: 10px;">
                        <a href="#">Forgot password?</a></p>
                </div>
            </fieldset>
    </LayoutTemplate>
</asp:Login>

As you can see, the offending line of code is marked in bold above.  I was using the TextBoxWatermarkExtender on the UserName TextBox control.  Since AJAX needs a NamingContainer, a simple work around is to simply wrap the code in a ASP.NET Panel control, and you’re problems will go away like so:

<asp:Login ID="Login2" runat="server" OnLoginError="Login2_LoginError">
    <LayoutTemplate>
            <fieldset>
                <div class="panel-username">
                    <p>
                        <asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName" CssClass="labeltext bold">Email Address:</asp:Label>

                        <asp:TextBox ID="UserName" runat="server" CssClass="textbox"></asp:TextBox>

                        <ajax:TextBoxWatermarkExtender ID="TextBoxWatermarkExtender1" runat="server" TargetControlID="UserName" WatermarkText="Enter Email Address" WatermarkCssClass="watermark">
                        </ajax:TextBoxWatermarkExtender>

                        <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName" ErrorMessage="User Name is required." ToolTip="User Name is required." ValidationGroup="ctl00$Login2">*</asp:RequiredFieldValidator>

                    </p>
                </div>

                <div class="panel-password">
                    <p>
                        <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password" CssClass="labeltext bold">Password:</asp:Label>

                        <asp:TextBox ID="Password" runat="server" TextMode="Password" CssClass="textbox"></asp:TextBox>

                        <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" ControlToValidate="Password" ErrorMessage="Password is required." ToolTip="Password is required." ValidationGroup="ctl00$Login2">*</asp:RequiredFieldValidator>

                    </p>
                </div>

                <div style="float: left; width: 150px;">
                    <p>
                        <span class="bold">Don't have an account?</span> <a id="A1" href="~/common/SignUp.aspx" runat="server">Register here</a>. It's easy and free!</p>

                </div>

                <div class="panel-button">
                    <p>
                        <asp:ImageButton ID="LoginButton" runat="server" CommandName="Login" ValidationGroup="ctl00$Login2" ImageUrl="~/Images/TransparentPixel.gif" CssClass="cmdSignIn" />
                    </p>

                    <p style="padding-top: 10px;">
                        <a href="#">Forgot password?</a></p>
                </div>
            </fieldset>
    </LayoutTemplate>
</asp:Login>

Now it works :)  Hope that helped anyone running into the same problem.  Whenever you’re dealing with ASP.NET template controls, and you’re using AJAX within these template controls, be sure to wrap the control pointed to by TargetControlId & the AJAX control itself in it’s own naming container to avoid issues like this.  Another bug down :rage:. :)

Comments

Leave a comment

Your email address will not be published. Required fields are marked *

Loading...