Standard ajax involves a heavy use of client side timers. Visual Web Gui shields developers from alot of the messy details, but there are times when you have to get messy. I want to filter data based on the value in a textbox. However, I don't want to require a button click or enter press, and I don't want to handle all the keypress events on the server as postbacks. Take this for example:

All I did was type kite, and some search results were returned. To accomplish this in Visual Web Gui, we could handle each textchanged event on the server, but this requires a postback for every key press. Not a good solution for the web. The original idea was to create a client side timer that would be started when the text was changed. I collaborated with Ryan Hatch, who did all of the coding, to put this together - thanks, Ryan!
Here is a live demonstration:
As you can see, we've got 2 ways to fire the events: while typing is occurring, or after typing is completed. That behavior is set here:

How does this work? First, a quick look at the Visual Studio Solution:

Ryan took the TextBox.js and TextBox.xslt files from the VWG Sources folder, modified them, and created a class that inherits from the TextBox class:
16 public partial class TimedTextBox : TextBox
17 {
18 public TimedTextBox()
19 {
20 InitializeComponent();
21 }
22
23 /// <summary>
24 /// Number of milliseconds that the timer will run before KeyPress event is raised
25 /// </summary>
26 [Description("Number of milliseconds that the timer will run before KeyPress event is raised")]
27 public int TimerLength { get; set; }
28
29 /// <summary>
30 /// Sets whether or not a running client timer will be reset when a key is pressed on the client.
31 /// </summary>
32 [Description("Sets whether or not a running client timer will be reset when a key is pressed on the client.")]
33 public bool ResetClientTimerOnKeyPress { get; set; }
34
35 protected override void RenderAttributes(Gizmox.WebGUI.Common.Interfaces.IContext objContext, Gizmox.WebGUI.Common.Interfaces.IAttributeWriter objWriter)
36 {
37 objWriter.WriteAttributeString("TimerLength", TimerLength);
38
39 if (this.ResetClientTimerOnKeyPress == true)
40 objWriter.WriteAttributeString("ResetClientTimerOnKeyPress", 1);
41 else
42 objWriter.WriteAttributeString("ResetClientTimerOnKeyPress", 0);
43
44 base.RenderAttributes(objContext, objWriter);
45 }
46
47 }
We added 2 properties:
- TimerLength: Number of milliseconds that the timer will run before code is executed
- ResetClientTimerOnKeyPress: A boolean value that sets whether or not a running client timer will be reset when a key is pressed on the client.
And we've overriden the RenderAttributes() method in the base class to include these properties as attributes in the html output. TimedTextBox.xslt: here we've added a an event handler to the html textbox, to call a javascript method:
71 <!--This is our hook into JavaScript, using OnKeyPress-->
72 <xsl:attribute name="onkeypress">mobjApp.TimedTextBox_SetTimer('<xsl:value-of select="@Id"/>',this,window);mobjApp.TextBox_KeyPress('<xsl:value-of select="@Id"/>','<xsl:value-of select="@Attr.CharacterValidationMask"/>','<xsl:value-of select="@Attr.CharacterValidationExpression"/>',window,this,event);</xsl:attribute>
and here are the javascript methods: TimedTextBox.js
15 // Called whenever the user changes text in textbox
16 function TimedTextBox_SetTimer(strGuid, objInput, objWindow) {
17
18 // Get Current TimerID
19 var myTimerID = Data_GetAttribute(strGuid, "TimerID");
20
21 // Get ResetTimerOnKeyPress Flag
22 var myResetTimerOnKeyPress = Data_GetAttribute(strGuid, "ResetClientTimerOnKeyPress");
23
24 // Is there a Timer already running?
25 var myHasTimerRunning = false;
26 if (myTimerID != null && myTimerID != 'null') { myHasTimerRunning = true; }
27
28 // Should we start a new Timer?
29 if (myResetTimerOnKeyPress == 1 || myHasTimerRunning == false) {
30
31 // Create TimerID
32 var myNewTimerID = TimedTextBox_CreateGuid();
33
34 // Set Current TimerID
35 Data_SetAttribute(strGuid, "TimerID", myNewTimerID);
36
37 // Create Callback
38 var myFunction = function() { TimedTextBox_TimerExpired(myNewTimerID, strGuid, objInput, objWindow); };
39
40 // Get Timer Length
41 var myTimerLength = Data_GetAttribute(strGuid, "TimerLength");
42
43 // Start Timer
44 var myTimeout = Web_SetTimeout(myFunction, myTimerLength);
45 }
The behavior of the textbox is determined by attributes that we set earlier. Line 44 invokes VWG code that starts a timer, and then executes code when the timer is complete. Here is the code when the timer is complete:
49 function TimedTextBox_TimerExpired(strTimerID, strGuid, objInput, objWindow) {
50
51 // Get Current TimerID
52 var myTimerID = Data_GetAttribute(strGuid, "TimerID");
53
54 // Is our TimerID the latest TimerID?
55 // We need this because - If user keeps typing, multiple timers will be running at once. Especially with the ResetClientTimerOnKeyPress flag
56 if (strTimerID == myTimerID) {
57
58 // Clear Timer - VWG Bug - setting null actually sets it to string 'null'!
59 Data_SetAttribute(strGuid, "TimerID", null);
60
61 // Raise Event (Fake the EnterKey to get TextChanged to Fire)
62 TextBox_Change(strGuid, objInput.value, objWindow, true);
63 }
By checking the TimerId value in line 56, we allow for the behavior to only send a call to the server if the id given for the timer matches what is currently stored in the textbox as an attribute. In line 62, we raise the Textbox_Change event. By the way, don't forget to declare the control in web.config:
41 <Controls>
42 <Control Type="MyControls.TimedTextBox.TimedTextBox, MyControls" />
43 <!--
44 <Control Type="Gizmox.WebGUI.Forms.Catalog.Controls.WinPanel, Gizmox.WebGUI.Forms.Catalog" />
45 <Control Type="Gizmox.WebGUI.Forms.WorkspaceTabs, Gizmox.WebGUI.Forms.Extended, Version=3.0.5701.0, Culture=neutral, PublicKeyToken=85eae29607c9f5f3" />
46 <Control Type="Gizmox.WebGUI.Forms.WatermarkTextBox, Gizmox.WebGUI.Forms.Extended, Version=3.0.5701.0, Culture=neutral, PublicKeyToken=85eae29607c9f5f3" />
47 <Control Type="Gizmox.WebGUI.Forms.SurfacePanel, Gizmox.WebGUI.Forms.Extended, Version=3.0.5701.0, Culture=neutral, PublicKeyToken=85eae29607c9f5f3" />
48 <Control Type="Gizmox.WebGUI.Forms.Editors.FCKEditor, Gizmox.WebGUI.Forms.Extended, Version=3.0.5701.0, Culture=neutral, PublicKeyToken=85eae29607c9f5f3" />
49 <Control Type="Gizmox.WebGUI.Forms.Charts.Chart, Gizmox.WebGUI.Forms.Charts, Version=3.0.5701.0, Culture=neutral, PublicKeyToken=f1bb83df6a8597fb" />
50 <Control Type="Gizmox.WebGUI.Forms.*, Gizmox.WebGUI.Forms.Office, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d50c2c7452ba77d9" />
51 -->
52 </Controls>
Caveat: Does not yet work on multiline textboxes.
This is a working concept that we hope can be integrated into the standard VWG textbox.
Download Code
Note: Codes are submitted as a .zip file to shorten your download time. After downloading it, you will need a program like Winzip to decompress it.
Terms of Agreement:
By using this code, you agree to the following terms...
- You may use this code in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
- You MAY NOT redistribute this code (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.
- You may link to this code from another website, but ONLY if it is not wrapped in a frame.
- You will abide by any additional copyright restrictions which the author may have placed in the code or code's description.