MargusLehiste
asked on
Issues with Custom PhoneBox Control
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._ar eacode)
' 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._pr efix)
' 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._la stfour)
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.IsClientScriptBloc kRegistere d("PhoneNu mberBoxScr ipt") 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.nextSiblin g.focus(); " & _
"}" & _
"</script>"
'js = "<script language='javascript' src='PhoneNumberBox.js'></ script>"
Me.Page.RegisterClientScri ptBlock("P honeNumber BoxScript" , 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.Ad d("onkeydo wn", "javascript:validateKey(); ")
Me._areacode.Attributes.Ad d("onkeyup ", "javascript:passFocus();")
Me._prefix.Attributes.Add( "onkeydown ", "javascript:validateKey(); ")
Me._prefix.Attributes.Add( "onkeyup", "javascript:passFocus();")
Me._lastfour.Attributes.Ad d("onkeydo wn", "javascript:validateKey(); ")
' render control.
MyBase.Render(writer)
End Sub
End Class
(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._ar
' 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._pr
' 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._la
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.IsClientScriptBloc
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.nextSiblin
"}" & _
"</script>"
'js = "<script language='javascript' src='PhoneNumberBox.js'></
Me.Page.RegisterClientScri
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.Ad
Me._areacode.Attributes.Ad
Me._prefix.Attributes.Add(
Me._prefix.Attributes.Add(
Me._lastfour.Attributes.Ad
' render control.
MyBase.Render(writer)
End Sub
End Class
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
John
jnhorst,
I already pointed that out in my first comment and corrected the code.
I already pointed that out in my first comment and corrected the code.
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, IsClientScriptBlockRegiste red 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
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, IsClientScriptBlockRegiste
John
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) IsClientScriptBlockRegiste red
I assume that when I call IsClientScriptBlockRegiste red 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(IsClientScriptBlockRegi stered(... )) 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. :-)
However, I disagree with you on the other points. Please do read my post and the code correctly.
1) IsClientScriptBlockRegiste
I assume that when I call IsClientScriptBlockRegiste
If(IsClientScriptBlockRegi
.. 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. :-)
1) IsClientScriptBlockRegiste red
I assume that when I call IsClientScriptBlockRegiste red 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(IsClientScriptBlockRegi stered(... )) Then
.. write out code
End If
=
1) IsClientScriptBlockRegiste red
I assume that when I call IsClientScriptBlockRegiste red 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(IsClientScriptBlockRegi stered(... )) Then
.. write out code
End If
and should actually be
If(Not IsClientScriptBlockRegiste red(...)) Then
.. write out code
End If
I assume that when I call IsClientScriptBlockRegiste
If(IsClientScriptBlockRegi
.. write out code
End If
=
1) IsClientScriptBlockRegiste
I assume that when I call IsClientScriptBlockRegiste
If(IsClientScriptBlockRegi
.. write out code
End If
and should actually be
If(Not IsClientScriptBlockRegiste
.. write out code
End If
Margus: I looked at my original code and CJ was correct in pointing out that
If Me.Page.IsClientScriptBloc kRegistere d("PhoneNu mberBoxScr ipt") Then
should be
If Not Me.Page.IsClientScriptBloc kRegistere d("PhoneNu mberBoxScr ipt") Then
John
If Me.Page.IsClientScriptBloc
should be
If Not Me.Page.IsClientScriptBloc
John
Let me look into the point at which you load and save viewstate and post here again.
John