Solved

Issues with Custom PhoneBox Control

Posted on 2004-10-20
9
334 Views
Last Modified: 2012-06-27
There are couple of issues that annoy me about the PhoneBox control I have
(it has parenthesis 3 textboxes - arecode, prefix, last 4 digits and dashes):
1) It does not keep viewstate and therefore the values inside cannot be retrieved.
2) When you type in area code 3 numbers - and press tab it moves to the next box _prefix - however when you press <SHIFT> + <TAB> and then release it - it loses the focus again.

Here is the code:

Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.ComponentModel
Public Class PhoneNumberControl
    Inherits WebControl
    'Right after the Inherits... statement, declare variables for your three text boxes:
    ' declare text boxes.
    Private _areacode As TextBox
    Private _prefix As TextBox
    Private _lastfour As TextBox

    <Bindable(True), Category("Appearance"), DefaultValue("")> ReadOnly Property [PhoneNo]() As String
        Get
            Return _areacode.Text
            If _areacode.Text.Length = 3 And _prefix.Text.Length = 3 And _lastfour.Text.Length = 4 Then
                Return "(" & _areacode.Text & ") " & _prefix.Text & "-" & _lastfour.Text
            Else
                Return ""
            End If
        End Get
    End Property

    <Bindable(False), Category("Appearance"), DefaultValue(0)> Overrides Property [tabIndex]() As Short
        Get
            Return _areacode.TabIndex()
        End Get
        Set(ByVal Value As Short)
            _areacode.TabIndex = Value
            _lastfour.TabIndex = Value
            _prefix.TabIndex = Value
        End Set
    End Property

    'Then add the constructor (often referred to as ctor).  In VB, this is the New() sub:  
    Public Sub New()
        ' make sure CreateChildControls gets called.
        MyBase.EnsureChildControls()
    End Sub

    'Now you need to override three routines of the base class (which is WebControl): CreateChildControls(); OnInit() and Render().  The CreateChildControls() routine is where you instantiate the textbox classes declared above and add them to the base class' Controls collection.  In your New() method, you tell the base class to call this method right when the class is instantiated.  EnsureChildControls() causes the base class to call your overriden CreateChildControls().  Here is what that routine should look like:
    Protected Overrides Sub CreateChildControls()
        MyBase.Controls.Add(New LiteralControl("("))

        ' instantiate and add the area code box.
        Me._areacode = New TextBox
        Me._areacode.MaxLength = 3
        Me._areacode.Width = Unit.Pixel(24)
        Me._areacode.Height = Unit.Pixel(18)
        Me._areacode.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._areacode)

        ' add a dash as a literal control.
        MyBase.Controls.Add(New LiteralControl(") "))

        ' instantiate and add the prefix box.
        Me._prefix = New TextBox
        Me._prefix.MaxLength = 3
        Me._prefix.Width = Unit.Pixel(24)
        Me._prefix.Height = Unit.Pixel(18)
        Me._prefix.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._prefix)

        ' add a dash as a literal control.
        MyBase.Controls.Add(New LiteralControl("-"))

        ' instantiate and add the last four digits box.
        Me._lastfour = New TextBox
        Me._lastfour.MaxLength = 4
        Me._lastfour.Width = Unit.Pixel(32)
        Me._lastfour.Height = Unit.Pixel(18)
        Me._lastfour.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._lastfour)
    End Sub

    'Now you want to override OnInit().  This is where you tell your page to check for the script block that points to your script file.  There is a function you will see below that checks to see if the script has already been registered.  This way if you put more than one of your control on a page, only one registration of the script block happens:
    Protected Overrides Sub OnInit(ByVal e As EventArgs)
        ' make sure client script block pointing to your script file is registered.
        If Me.Page.IsClientScriptBlockRegistered("PhoneNumberBoxScript") Then
            Dim js As String = "<script language='javascript'>" & _
" function validateKey()" & _
" {" & _
"     var key = window.event.keyCode; " & _
"     var test1 = (key >= 48     && key <= 57);  " & _
"     var test2 = (key >= 96     && key <= 105); " & _
"     var test3 = (key == 37     || key == 39);  " & _
"     var test4 = (key == 46     || key == 9);   " & _
"     var test5 = (key == 8); " & _
" " & _
"     if (!test1 && !test2 && !test3 && !test4 && !test5)" & _
"     {" & _
"          window.event.returnValue = false;" & _
"          window.event.cancelBubble = true;" & _
"     }" & _
"}" & _
"" & _
"function passFocus()" & _
"{" & _
"     var src = window.event.srcElement;" & _
"     var myMaxLen = maxlength; " & _
"     var maxlen = src.getAttribute(myMaxLen); " & _
"     if (src.value.length == maxlen && window.event.keyCode != 16 && window.event.keyCode != 17) src.nextSibling.nextSibling.focus();" & _
"}" & _
"</script>"

            'js = "<script language='javascript' src='PhoneNumberBox.js'></script>"
            Me.Page.RegisterClientScriptBlock("PhoneNumberBoxScript", js)
        End If

        ' let base complete the init process.
        MyBase.OnInit(e)
    End Sub

    'Here is one of the funny things about VB.NET.  These two script registering methods do not appear in Intellisense (they do appear in C#, go figure).  But they are there, just make sure your spelling is right.
    'Lastly, you will override Render().  By this time everything has been instantiated.  Your base class has it's id string, so it can pass it along to the three text box controls.  This is also where you add attributes to the text boxes so they call your JavaScript functions when a key has been pressed (onkeydown) to disallow non numeric characters and when the key comes up (onkeyup) to check the length of the value.  If it is the same as the maxlength property of the text box (3, 3, and 4 respectively), then it passes focus to the next box.  Here it is:

    Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
        'set ids.
        Me._areacode.ID = Me.ID & "_areacode"
        Me._prefix.ID = Me.ID & "_prefix"
        Me._lastfour.ID = Me.ID & "_lastfour"

        ' set attributes.
        Me._areacode.Attributes.Add("onkeydown", "javascript:validateKey();")
        Me._areacode.Attributes.Add("onkeyup", "javascript:passFocus();")
        Me._prefix.Attributes.Add("onkeydown", "javascript:validateKey();")
        Me._prefix.Attributes.Add("onkeyup", "javascript:passFocus();")
        Me._lastfour.Attributes.Add("onkeydown", "javascript:validateKey();")

        ' render control.
        MyBase.Render(writer)
    End Sub

End Class
0
Comment
Question by:MargusLehiste
  • 5
  • 4
9 Comments
 
LVL 22

Accepted Solution

by:
CJ_S earned 300 total points
Comment Utility
Couple of problems.
1) you assigned your textbox controls names
2) you should use the INamingContainer
3) you didn't check for Not IsRegistered...
4) the property is this.maxLength, not maxlength

Here's the correct code:

Public Class PhoneNumberControl
    Inherits WebControl
    Implements INamingContainer
   
    'Right after the Inherits... statement, declare variables for your three text boxes:
    ' declare text boxes.
    Private _areacode As TextBox
    Private _prefix As TextBox
    Private _lastfour As TextBox

    <Bindable(True), Category("Appearance"), DefaultValue("")> ReadOnly Property [PhoneNo]() As String
        Get
            Return _areacode.Text
            If _areacode.Text.Length = 3 And _prefix.Text.Length = 3 And _lastfour.Text.Length = 4 Then
                Return "(" & _areacode.Text & ") " & _prefix.Text & "-" & _lastfour.Text
            Else
                Return ""
            End If
        End Get
    End Property

    <Bindable(False), Category("Appearance"), DefaultValue(0)> Overrides Property [tabIndex]() As Short
        Get
            Return _areacode.TabIndex()
        End Get
        Set(ByVal Value As Short)
            _areacode.TabIndex = Value
            _lastfour.TabIndex = Value
            _prefix.TabIndex = Value
        End Set
    End Property

    'Then add the constructor (often referred to as ctor).  In VB, this is the New() sub:  
    Public Sub New()
        ' make sure CreateChildControls gets called.
        MyBase.EnsureChildControls()
    End Sub

    'Now you need to override three routines of the base class (which is WebControl): CreateChildControls(); OnInit() and Render().  The CreateChildControls() routine is where you instantiate the textbox classes declared above and add them to the base class' Controls collection.  In your New() method, you tell the base class to call this method right when the class is instantiated.  EnsureChildControls() causes the base class to call your overriden CreateChildControls().  Here is what that routine should look like:
    Protected Overrides Sub CreateChildControls()
        MyBase.Controls.Add(New LiteralControl("("))

        ' instantiate and add the area code box.
        Me._areacode = New TextBox
        Me._areacode.MaxLength = 3
        Me._areacode.Width = Unit.Pixel(24)
        Me._areacode.Height = Unit.Pixel(18)
        Me._areacode.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._areacode)

        ' add a dash as a literal control.
        MyBase.Controls.Add(New LiteralControl(") "))

        ' instantiate and add the prefix box.
        Me._prefix = New TextBox
        Me._prefix.MaxLength = 3
        Me._prefix.Width = Unit.Pixel(24)
        Me._prefix.Height = Unit.Pixel(18)
        Me._prefix.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._prefix)

        ' add a dash as a literal control.
        MyBase.Controls.Add(New LiteralControl("-"))

        ' instantiate and add the last four digits box.
        Me._lastfour = New TextBox
        Me._lastfour.MaxLength = 4
        Me._lastfour.Width = Unit.Pixel(32)
        Me._lastfour.Height = Unit.Pixel(18)
        Me._lastfour.CssClass = "standardFormType"
        MyBase.Controls.Add(Me._lastfour)
    End Sub

    'Now you want to override OnInit().  This is where you tell your page to check for the script block that points to your script file.  There is a function you will see below that checks to see if the script has already been registered.  This way if you put more than one of your control on a page, only one registration of the script block happens:
    Protected Overrides Sub OnInit(ByVal e As EventArgs)
        ' make sure client script block pointing to your script file is registered.
        If(Not Me.Page.IsClientScriptBlockRegistered("PhoneNumberBoxScript")) Then
            Dim js As String = "<script language='javascript'>" & _
" function validateKey()" & _
" {" & _
"     var key = window.event.keyCode; " & _
"     var test1 = (key >= 48     && key <= 57);  " & _
"     var test2 = (key >= 96     && key <= 105); " & _
"     var test3 = (key == 37     || key == 39);  " & _
"     var test4 = (key == 46     || key == 9);   " & _
"     var test5 = (key == 8); " & _
" " & _
"     if (!test1 && !test2 && !test3 && !test4 && !test5)" & _
"     {" & _
"          window.event.returnValue = false;" & _
"          window.event.cancelBubble = true;" & _
"     }" & _
"}" & _
"" & _
"function passFocus()" & _
"{" & _
"     var src = window.event.srcElement;" & _
"     var myMaxLen = this.maxLength; " & _
"     var maxlen = src.getAttribute(myMaxLen); " & _
"     if (src.value.length == maxlen && window.event.keyCode != 16 && window.event.keyCode != 17) src.nextSibling.nextSibling.focus();" & _
"}" & _
"</script>"

            'js = "<script language='javascript' src='PhoneNumberBox.js'></script>"
            Me.Page.RegisterClientScriptBlock("PhoneNumberBoxScript", js)
        End If

        ' let base complete the init process.
        MyBase.OnInit(e)
    End Sub

    'Here is one of the funny things about VB.NET.  These two script registering methods do not appear in Intellisense (they do appear in C#, go figure).  But they are there, just make sure your spelling is right.
    'Lastly, you will override Render().  By this time everything has been instantiated.  Your base class has it's id string, so it can pass it along to the three text box controls.  This is also where you add attributes to the text boxes so they call your JavaScript functions when a key has been pressed (onkeydown) to disallow non numeric characters and when the key comes up (onkeyup) to check the length of the value.  If it is the same as the maxlength property of the text box (3, 3, and 4 respectively), then it passes focus to the next box.  Here it is:

    Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
        ' set attributes.
        Me._areacode.Attributes.Add("onkeydown", "javascript:validateKey();")
        Me._areacode.Attributes.Add("onkeyup", "javascript:passFocus();")
        Me._prefix.Attributes.Add("onkeydown", "javascript:validateKey();")
        Me._prefix.Attributes.Add("onkeyup", "javascript:passFocus();")
        Me._lastfour.Attributes.Add("onkeydown", "javascript:validateKey();")

        ' render control.
        MyBase.Render(writer)
    End Sub

End Class
0
 
LVL 10

Expert Comment

by:jnhorst
Comment Utility
Margus... you probably should take out the comments intervening the various parts of the code.  I provided those with the code so you would understand the reasoning behind each step.  You can keep em in there if you like, but it clutters things up a bit.

Let me look into the point at which you load and save viewstate and post here again.

John
0
 
LVL 10

Assisted Solution

by:jnhorst
jnhorst earned 200 total points
Comment Utility
Check this article out.  Review the part about overriding LoadViewState and SaveViewState in order for your control to maintain its state.  I'll look further into this after work and check to see if you have been able to add that to the control.

John
0
 
LVL 10

Expert Comment

by:jnhorst
Comment Utility
Also, I think your override of tabIndex is part if your problem.  You are assigning all three textboxes the same tab index.  The prefix should have an index 1 higher than the area code, and the last four 1 higher than the last four digits.

John
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 22

Expert Comment

by:CJ_S
Comment Utility
jnhorst,
I already pointed that out in my first comment and corrected the code.
0
 
LVL 10

Expert Comment

by:jnhorst
Comment Utility
CJ_S:

A few things here.  This is the result of a previous question.  I provided Margus with this code to get him started with a custom control.  Second: I do not see in your code edit where you addressed the TabIndex issue as I did above.  In your post, all three text boxes are getting the same tab index.  Third: If you look carefully at the original OnInit() override, IsClientScriptBlockRegistered is called to check if the script has already been registered.  Fourth: You took out the code assigning the text boxes id strings.  I put that there so the control could be used more than once on a page without running into problems.  If he tries to put this in a TemplateColumn of a grid or list, this will be important.  Fourth, his use of maxlen in the JavaScript routine is a variable that is declared and assigned immediately above in the code.

John
0
 
LVL 22

Expert Comment

by:CJ_S
Comment Utility
So you're more into the code than I am, or at least know exactly what MargusLehiste wants. So you may be right about the TabIndex, I do not know what he wants, but it does seem to tab correctly to me.

However, I disagree with you on the other points. Please do read my post and the code correctly.

1) IsClientScriptBlockRegistered
I assume that when I call IsClientScriptBlockRegistered that I get back a boolean value indicating whether it IS or is NOT registered. In the original code you will see that it has
If(IsClientScriptBlockRegistered(...)) Then
  .. write out code
End If

2) >> Fourth: You took out the code assigning the text boxes id strings.  I put that there so the control could be used more than once on a page without running into problems.  If he tries to put this in a TemplateColumn of a grid or list, this will be important.
Read about the INamingContainer, that way the controls will get correct naming by itself, as it should be. And thus also work in repeaters and other databound controls.

3) >> Fourth, his use of maxlen in the JavaScript routine is a variable that is declared and assigned immediately above in the code.
maxlen is indeed a variable, but maxlength was not. maxlength is a property of the textbox and should actually be written with a capital L (maxLength). Since it is a property of the object owning the event I prefer to add an additional 'this.' to the property.

Try running my code if you want.

CJ. :-)
0
 
LVL 22

Expert Comment

by:CJ_S
Comment Utility
1) IsClientScriptBlockRegistered
I assume that when I call IsClientScriptBlockRegistered that I get back a boolean value indicating whether it IS or is NOT registered. In the original code you will see that it has
If(IsClientScriptBlockRegistered(...)) Then
  .. write out code
End If

=

1) IsClientScriptBlockRegistered
I assume that when I call IsClientScriptBlockRegistered that I get back a boolean value indicating whether it IS or is NOT registered. In the original code you will see that it has
If(IsClientScriptBlockRegistered(...)) Then
  .. write out code
End If

and should actually be
If(Not IsClientScriptBlockRegistered(...)) Then
  .. write out code
End If
0
 
LVL 10

Expert Comment

by:jnhorst
Comment Utility
Margus:  I looked at my original code and CJ was correct in pointing out that

If Me.Page.IsClientScriptBlockRegistered("PhoneNumberBoxScript") Then

should be

If Not Me.Page.IsClientScriptBlockRegistered("PhoneNumberBoxScript") Then

John
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

I recently went through the process of creating a Calendar Control of events with the basis of using a database to keep track of the dates that are selectable, one requirement was to have the selected date pop-up in a simple lightbox.  At first this…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This video discusses moving either the default database or any database to a new volume.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now