in

ASP.NET Weblogs

Casey Roach

  • RegisterHiddenField within dynamically created controls

    I've been building a composite control that has a client-side object associated with it.  Any changes on the client are persisted back to the server through a hidden field and the server-side object is updated with these changes.  I register the hidden field with the page in the OnPreRender event of the control.  This works well with statically created controls.

    I ran into trouble when I was creating these controls dynamically.  My controls were being created and added to the Page on every postback in the Page's OnInit event.  However, when it came time for the control to save its client state in the hidden field, the hidden field couldn't be found!

    What's wrong here? Here is a snippet from my control's code:

    protected override void OnPreRender(EventArgs e)
    {
         ScriptManager sm = ScriptManager.GetCurrent(Page);
         if (sm == null)
         {
              throw new InvalidOperationException("A ScriptManager is required on this page");
         }
         base.OnPreRender(e);
         ClientScript.RegisterHiddenField(this.ClientStateID, "");
    }
    

    Nothing! (at least that's what I thought). See if you can spot the difference in this next snippet:

    protected override void OnPreRender(EventArgs e)
    {
         ScriptManager sm = ScriptManager.GetCurrent(Page);
         if (sm == null)
         {
              throw new InvalidOperationException("A ScriptManager is required on this page");
         }
         base.OnPreRender(e);
         ScriptManager.RegisterHiddenField(this, this.ClientStateID, "");
    }
    

    After cracking open the source code to the AjaxControlToolkit, I saw that the experts were calling the ScriptManager's static method in order to register the hidden field with the page.  I'm not sure why you can't call the Page's ClientScript's method instead, but just in case anyone else runs up against this, I thought I'd throw this out there to help.

  • Dismiss the ModalPopupExtender with Esc or Enter key

    I have a ModalPopupExtender attached to a button on one of the pages I'm working on.  The extender targets a Panel defined here:

        1 <asp:Panel ID="CaseNumberPanel" runat="server" style="display:none;" CssClass="modalPopup">

        2     <asp:Panel ID="CaseNumberHeader" runat="server" Style="cursor: move; background-color: #DDD;

        3         border: solid 1px Gray;">

        4         <div style="text-align:center;">

        5             Enter the Case Number

        6         </div>

        7     </asp:Panel>

        8     <div>

        9         <asp:TextBox ID="CaseNum" runat="server" Width="169px" style="text-transform:uppercase;" MaxLength="20"></asp:TextBox>

       10         <ajax:MaskedEditExtender ID="CaseNumMask" runat="server" TargetControlID="CaseNum" ClearMaskOnLostFocus="false"

       11             Mask="LL-99-9999999999" ></ajax:MaskedEditExtender>

       12 

       13         <ajax:MaskedEditValidator ID="CaseNumValidator" runat="server" SetFocusOnError="true" ControlToValidate="CaseNum"

       14                     ControlExtender="CaseNumMask" Display="dynamic" ClientValidationFunction="ValidateCaseNumber"

       15                     EmptyValueMessage="Case Number required" IsValidEmpty="false" ValidationGroup="CaseNum"

       16                     InvalidValueMessage="Invalid Case Number"></ajax:MaskedEditValidator>

       17         <div style="text-align: center;">

       18             <asp:Button ID="CaseNumberOk" runat="server" Width="50px" Text="OK" CssClass="NewButton" />

       19             <asp:Button ID="CaseNumberCancel" runat="server" Width="50px" Text="Cancel" CssClass="NewButton" />

       20         </div>

       21     </div>

       22 </asp:Panel>

    As you can see, I'm using the ModalPopupExtender solely to ensure the correct user input before being redirected to another page.  The input here is the main focus for everything that happens in the next page.

    I wanted the focus to be on the TextBox when the popup was shown, but couldn't find anything that helped.  So, instead, I pored over the source of the toolkit and found the event handler I needed in the ModalPopupBehavior.js file.  Basically, you want to hook into the 'shown' event.  You would do this in the pageLoad event in javascript like this:

        1 function pageLoad(sender,e)

        2 {

        3     $find('CaseNumberModal').add_shown(OnModalPopup);

        4     var caseBox = $get('<%= CaseNum.ClientID %>');

        5     $addHandler(caseBox, 'keydown', CaseNumKeyDown);

        6     $addHandler(document, 'keydown',CaseNumKeyDown);

        7 }

    You can alse see here that I'm hooking into the keydown events that are focused on the TextBox or anywhere else in the document.  From what I understand, you have to hook into this event and not the 'keypress' event, since some browsers don't fire that event for the Esc or Enter keys.

    Now the we have hooked into our events, we can define the handlers.

        1 function CaseNumKeyDown(e)

        2 {

        3     if(e.keyCode == Sys.UI.Key.enter)

        4     {

        5         // Trap the enter key so we can trigger validation

        6         // and the okScript code for the extender

        7         e.preventDefault();

        8         e.stopPropagation();

        9         $common.tryFireEvent($get('<%= CaseNum.ClientID %>'), 'change');

       10         ValidatorOnSubmit();           

       11         if(Page_IsValid)

       12             $find('CaseNumberModal')._onOk(e);

       13         return false;

       14     }

       15     else if(e.keyCode == Sys.UI.Key.esc)

       16     {

       17         // Trap the esc key so we can trigger the cancelScript action

       18         var ext = $find('CaseNumberModal');

       19         ext._onCancel(e);

       20         e.stopPropagation();

       21     }

       22 }

       23 function OnModalPopup(e)

       24 {

       25     $get('<%= CaseNum.ClientID %>').focus();

       26 }

    The focus for the TextBox is easy.  Once we hook into the 'shown' event of the ModalPopupExtender, we can find the TextBox and focus control on it.

    The KeyDown event was a little bit trickier to figure out.  The behavior I wanted was for the Esc key to perform the same operation as the cancel button and the Enter key to perform the same operation as the OK button.  Unfortunately, you can't just say $get('MyOkButton').click().  While the page will Post Back, validation and any OkScript defined in the ModalPopupExtender are not happening.  This is the same with the cancel button.

    So I had perform any validation, and then call an scripts defined in the OK/cancel events.  First, we'll focus on the Enter key press handling.  Looking at the markup of a page, I found the javascript function ValidatorOnSubmit().  This ensures that any client-side validation is occurring before anything posts back to the server.  This wasn't enough, though.  The validation I had tied to the TextBox was not occurring.  I reasoned that the TextBox probably couldn't fire the 'change' event before I started looking at the key-presses.  The only logical thing to do then was to fire the 'change' event on the TextBox in order to trigger the client-side validation behavior, then call the ValidatorOnSubmit() function.  I then fire the ok event of the ModalPopupExtender and let it take care of itself..  If you've never seen the 'Page_IsValid' variable before, then you should know that it's defined by ASP.NET and allows you to get/set whether a page is valid at any given point.

    If the user hits the Esc key, then I want to trigger the cancel script defined in my ModalPopupExtender.  In both cases, you have to stop the propagation of the event so that any DefaultButtons defined in the form aren't clicked.

    I hope this helps.

  • Problems with GridView and a Database persisted Viewstate

     

    I'm currently working on a project that requires a lot of controls to be displayed on a single page, with a lot of user input.  This means I'm using a few UpdatePanels wrapped around this content.  Most of the controls are user editable, similar to a data entry screen.  What this also means is a large Viewstate field being posted back to my server each time I update any of the content.

    Among this content, are a few GridViews I've been working with.  One in particular recently broke after I changed a few things on the page.  In particular, I was no longer able to update a row.  I used to be able to (and it worked beautifully), but no longer.  Take a look at the following code:

       88 <asp:GridView ID="CodeGrid" runat="server" CssClass="Codes" AutoGenerateColumns="False"

       89     OnRowCommand="CodeGrid_RowCommand" DataSourceID="SqlDataSource1" ShowFooter="True"

       90     DataKeyNames="PK" OnRowUpdating="CodeGrid_RowUpdating" EnableViewState="false">

       91     <Columns>

       92         <asp:TemplateField HeaderText="PK" Visible="false">

       93             <ItemTemplate>

       94                 <%# Eval("PK") %>

       95             </ItemTemplate>

       96         </asp:TemplateField>

       97         <asp:TemplateField HeaderText="Code Section">

       98             <ItemTemplate>

       99                 <%# Eval("CodeSection") %>

      100             </ItemTemplate>

      101             <EditItemTemplate>

      102                 <asp:TextBox ID="EditCodeSection" CssClass="CodeTextBox" runat="server" MaxLength="12"

      103                     Text='<%# Bind("CodeSection") %>' />

      104             </EditItemTemplate>

      105             <FooterTemplate>

      106                 <asp:TextBox ID="InsertCodeSection" CssClass="CodeTextBox" runat="server" MaxLength="12"

      107                     Text='<%# Bind("CodeSection") %>' />

      108             </FooterTemplate>

      109         </asp:TemplateField>

      110         <asp:TemplateField HeaderText="Offense Code">

      111             <ItemTemplate>

      112                 <%# Eval("OffenseCode") %>

      113             </ItemTemplate>

      114             <EditItemTemplate>

      115                 <asp:TextBox ID="EditOffenseCode" Width="95%" CssClass="CodeTextBox" runat="server"

      116                     MaxLength="6" Text='<%# Bind("OffenseCode") %>' />

      117             </EditItemTemplate>

      118             <FooterTemplate>

      119                 <asp:TextBox ID="InsertOffenseCode" Width="95%" CssClass="CodeTextBox" runat="server"

      120                     MaxLength="6" Text='<%# Bind("OffenseCode") %>' />

      121             </FooterTemplate>

      122         </asp:TemplateField>

      123         <asp:TemplateField HeaderText="Description">

      124             <ItemTemplate>

      125                 <%# Eval("Description") %>

      126             </ItemTemplate>

      127         </asp:TemplateField>

      128         <asp:TemplateField HeaderText="Counts">

      129             <ItemTemplate>

      130                 <%# DisplayCounts( Eval("Counts","{0:d}"))%>

      131             </ItemTemplate>

      132             <EditItemTemplate>

      133                 <asp:TextBox ID="EditCounts" Width="90%" CssClass="CodeTextBox" runat="server" MaxLength="2"

      134                     Text='<%# Bind("Counts") %>' />

      135             </EditItemTemplate>

      136             <FooterTemplate>

      137                 <asp:TextBox ID="InsertCounts" Width="90%" CssClass="CodeTextBox" runat="server"

      138                     MaxLength="2" Text='<%# Bind("Counts") %>' />

      139             </FooterTemplate>

      140         </asp:TemplateField>

      141         <asp:TemplateField>

      142             <ItemStyle CssClass="GridButton" />

      143             <ItemTemplate>

      144                 <asp:Button CssClass="GridButtons" runat="server" ID="Edit" Text="Edit" CommandName="Edit" />

      145                 <asp:Button CssClass="GridButtons" runat="server" ID="Delete" Text="Delete" CommandName="Delete" />

      146             </ItemTemplate>

      147             <EditItemTemplate>

      148                 <asp:Button CssClass="GridButtons" runat="server" ID="Update" Text="Save" CommandName="Save" />

      149                 <asp:Button CssClass="GridButtons" runat="server" ID="Cancel" Text="Cancel" CommandName="Cancel" />

      150             </EditItemTemplate>

      151             <FooterTemplate>

      152                 <div style="text-align: center;">

      153                     <asp:Button CssClass="GridButtons" runat="server" ID="Insert" Text="Add" CommandName="InsertNew" />

      154                     <asp:Button CssClass="GridButtons" runat="server" ID="Cancel" Text="Cancel" CommandName="CancelNew" />

      155                 </div>

      156             </FooterTemplate>

      157         </asp:TemplateField>

      158     </Columns>

      159     <EmptyDataRowStyle CssClass="EmptyCodeRow" />

      160     <RowStyle CssClass="CodeRow" />

      161     <EditRowStyle CssClass="EditCodeRow" />

      162     <SelectedRowStyle CssClass="SelectedCodeRow" />

      163     <HeaderStyle CssClass="CodeHeader" />

      164     <AlternatingRowStyle CssClass="AltCodeRow" />

      165     <EmptyDataTemplate>

      166         Code Section:<asp:TextBox CssClass="CodeTextBox" runat="server" ID="NoDataCodeSection"

      167             Width="20%" />

      168         Offense:<asp:TextBox CssClass="CodeTextBox" runat="server" ID="NoDataOffenseCode"

      169             Width="15%" />

      170         Counts:<asp:TextBox CssClass="CodeTextBox" runat="server" ID="NoDataCounts" Width="10%" />

      171         &nbsp;&nbsp;&nbsp;<asp:Button CssClass="GridButtons" runat="server" ID="NoDataInsert"

      172             CommandName="NoDataInsert" Text="Add" />

      173     </EmptyDataTemplate>

      174 </asp:GridView>

     

    Ok, so it’s a lot of code, but I feel you should see the entire thing to get an idea of what I’m working with.  The SqlDataSource I’m referencing as the Data source has valid update, select, insert, and delete commands along with the correct parameters.  I changed nothing in the GridView or the SqlDataSource between when it was working and when it broke.

    What I did, however is try a different method of persisting the ViewState other than on the page itself.  While looking around, I found a post by Adrian O’Connor where he shows a method of saving/loading the ViewState from SQL server.  I implemented his approach a couple of weeks ago, along with quite a few other changes to the UI.  This week, however, I noticed the OnRowCommand event of the GridView was not firing when I clicked the ‘Save’ button within the EditItemTemplate on line 157.  The ‘Edit’ button fired the event fine, as did the ‘Delete’, but it seemed that whenever there was a change to the initial markup of the GridView, the OnRowCommand event would not fire. 

    I spent two days looking for answers, of which multiple Databinding issues were usually the problem.  None of the solutions I found worked for me.  After two days of working on the problem, I discovered that it was the Viewstate change I made that was causing the problem.  I had overridden the PageStatePersister property on the page to use the custom PageStatePersister I created from Adrian’s blog.  After removing this property and letting the ViewState store itself on the page like normal, the OnRowCommand event was not firing for the update command.

    I don’t know why this caused the GridView’s natural functioning to break.  The ‘Save’ button was still posting back to the server as I saw it in the Page_Load method, but it was never hitting the OnRowCommand or any Update events either.  I’m guessing that the ViewState was not being updated properly so that the page wasn’t expecting the ‘Save’ button’s click/OnRowCommand event.

    Until I find out why this is happening, I’ve removed the custom ViewState Persister, and am going to rely on storing the ViewState on the page.  Maybe someone out there can explain to me why this is happening.

     
More Posts