Thursday, December 20, 2012

Preventing Postback in a textfield with the Enter key for submit–Disable the enter key (for certain fields on a form)

Hack the Gibson

Recently, I received a phone call on one of the applications that I was working on that it was creating erroneous data and thus causing data integrity problems.  In my best problem solving (fault isolating) hat, I decided to get to the bottom of the problem.  In my discovery, I figured out that what was happening was as the user was entering data, instead of clicking (or tabbing) to the next field, they were simply pressing Enter.  We as developers, know this will cause a postback to the server and a submit of the form.  Thus, without any validity checks can cause your friendly neighborhood DBA some major headaches.

To further complicate the problem, I did not want to lock down the form to require information to be populated in all fields as this information was used in helping to categorize the record but as time went on would be populated.  Once all the data is populated, the record was expunged into an archive.  It is an issue tracking application specific to the investment world, but was needed.  So having this prerequisite, I could not simply add a check to make sure all fields were populated.  This left me with only one option, which was to disable the enter key.

Here’s the kicker, I have a lot of TextBoxes on the form, but some of them are multi-lined.  So simply performing a universal disable of the enter key was not an option.  Now what?  Ok, I know the IDs of the fields I don’t want to disable the enter key on.  Is that enough?  Turns out yes.

In my research to solve this problem, I read pretty much on every post on the subject that you can not have it both ways.  You can not disable the enter button on some textboxes but not others.  I immediately threw a bullshit flag on that play.  Why can’t I?  They are individual objects on the form.  Unless I disable the enter key from the form’s scope, I should be able to disable it for some controls but not all.  How do I accomplish this?  A loop of some sort would work perfectly here.  If I could loop through all the controls on the form – Aha! Yes! Turns out, that’s exactly what we need to do.

So now we introduce a new problem.  I am using Content Pages.  If I simply pass in the Page object, it references the page from the masterpage perspective.  All I want is the controls that exist in the child (content) page.  So the first task is to create a variable referencing the ContentPlaceHolder control.  Due to the fact that I am using Content Pages (child to the MasterPage) all the controls I put on this particular page exist (when rendering) in the ContentPlaceHolder.  So we need to hit the MasterPage and then work our way down to the ContentPlaceHolder control that is on it.  Obviously, controls can contain other control objects which is the idea here.  Once we have a reference to the ContentPlaceHolder, we have a grip on the controls that exist inside it.
So our code to get the ContentPlaceHolder will look like:

Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)




Once we have our ContentPlaceHolder control (which we assigned to the content variable) we can loop through all the controls that are contained in the control.  This is done by using the Controls array parameter.  In our case, this would be written as content.Controls.  So now we just need to loop through those:



Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
Next




In the For Each loop, what we are doing is going through the array one by one, and as we hit each element of the array we are assigning it the cntrl variable so that we can manipulate the control on a singular (array element) context.  However, this particular variable only exists as long as we are in that particular iteration of the For Each Loop.  Once we hit the NEXT iteration, the variable is reassigned to the next array element. 
Now that we are looping through all the controls in the ContentPlaceHolder, we only want to disable the enter key for text fields, so we need to check for that:




Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
End If
Next




Now we are checking to see if the TypeOf control is a TextBox.  This is pretty self-explanatory code.  From here, we need to get a handle on the actual control.  cntrl is a variable that holds the array element.  We need to create a variable that is a representation of the actual control itself:



Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
Dim
t As TextBox = TryCast(cntrl, TextBox)
End If
Next




t represents the actual TextBox that exists in your HTML.  Thus, if your first field on the Content Page is txtFirstName, the ID of t (or t.ID) = “txtFirstName”.  This is a programmatic representation through a variable of the TextBox object.  You can code it much like you could any TextBox control in code behind.  For instance to change the ID of the control from txtFirstName to txtFirstNameOfUser:



t.ID = "txtFirstNameOfUser"




Of course all the TextBox properties are available for use for instance, Text, ID, Attributes… etc..

This is important because we can now manipulate the control in code behind (for instance we can disable the enter key for this TextBox) – Aha!


So how do we disable the TextBox from submitting on Enter?  We use its Attribute property, like so:




Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
Dim
t As TextBox = TryCast(cntrl, TextBox)
t.Attributes.Add("onkeydown", "return (event.keyCode!=13);")
End If
Next
End Sub




Ok, so this disables every single TextBox’s Enter key ( Honestly, what it is doing is if the keyCode is NOT 13 it renders true, thus completing the keyCode, otherwise it renders false and does not).  How do we disable it only for certain TextBoxes?  We need to know the ID of the TextBoxes we want to bypass:



Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
Dim
t As TextBox = TryCast(cntrl, TextBox)
If Not (t.ID = "txtComments") Then
t.Attributes.Add("onkeydown", "return (event.keyCode!=13);")
End If
End If
Next
End Sub




In this case, the TextBox with ID = “txtComments” will be able to hit enter.  This is needed for the multi-lined TextBox.  What if we have more than one:



Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
Dim
t As TextBox = TryCast(cntrl, TextBox)
If Not (t.ID = "txtComments" Or _
t.ID = "txtSubject") Then
t.Attributes.Add("onkeydown", "return (event.keyCode!=13);")
End If
End If
Next




How do we use this code?

Well we can put it into the Page.Load event for the page.  This code is run after the page has been constructed but before it is rendered to the user, so the objects physically exist on the page, but it has not been served to the client browser yet.


However, I like to keep my Page.Load clean so I usually create a Sub and then reference it in the Page.Load, Like so:




Protected Sub Page_Load(sender As Object, _
e As System.EventArgs) Handles Me.Load
BlockEnterKey()
End Sub
Private Sub BlockEnterKey()
Dim content As ContentPlaceHolder = _
DirectCast(Me.Master.FindControl("ContentPlaceHolder1"), ContentPlaceHolder)
For Each cntrl As Control In content.Controls
If TypeOf cntrl Is TextBox Then
Dim
t As TextBox = TryCast(cntrl, TextBox)
If Not (t.ID = "txtComments" Or _
t.ID = "txtSubject") Then
t.Attributes.Add("onkeydown", "return (event.keyCode!=13);")
End If
End If
Next
End Sub




So now we can disable the enter key on certain TextBoxes while leaving other ones alone.  We can also expand this, if it is a CheckBoxList to select the first item or basically manipulate any control we want on the form.  Just whatever you need in a particular instance.




image   Håþþ¥ .ñꆆïñg…

No comments:

Post a Comment