Introduction
If you�ve been following along with this article series then you know in Part 1 we talked briefly about Asynchronous JavaScript And XML or AJAX for short. We created the CallBackObject
to help simplify the callback initiation process. In Part 2 we looked at integrating the CallBackObject
with ASP.NET controls and events. Now, in Part 3, we are going to put all that knowledge to good use and create a custom ASP.NET control using everything we�ve learned so far. That control, as you may have guessed, is the Auto Complete TextBox (ACTB
).
Before we get to the good stuff I want to say there is a lot of room for improvement for this control. Functionality, design-time integration, and ease-of-use can all be drastically improved. I leave that to you!
The Good Stuff
Before we talk about creating the control, I want to show you just how easy it is to use the control. We�ll start with Listings 1 and 2 (Actb.aspx and Actb.aspx.cs). After you calm down from your overwhelming excitement, we will examine the JavaScript portion of the ACTB Listing 3 (AutoCompleteTextBox.js) and the ASP.NET portion Listing 4 (AutoCompleteTextBox.cs).
Actb.aspx
This page is pretty straightforward. We are setting up some input fields for the user. Most of you will probably note that not all countries use a Zip code. I put this in as a demonstration of how you might customize the results of the Auto Complete TextBox based on values from other fields. (more on this later.) To use our custom control we have to register the assembly with the page and give it a tag prefix. I�ve modestly used my initials as the tag prefix but you can choose any prefix you desire. We also specify the namespace and assembly name where the control(s) can be found.
<%@ Register TagPrefix="wcp" Namespace="WCPierce.Web.UI.WebControls"
Assembly="WCPierce.Web" %>
After that, the only interesting piece of HTML is the actual declaration of the ACTB
.
<wcp:AutoCompleteTextBox runat="server" id="actbCountry"
OnTextChanged="actbCountry_TextChanged" ListItemCssClass="ListItem"
ListItemHoverCssClass="ListItemHover" />
The declaration is nearly identical to that of a normal ASP TextBox
. The only added attributes are the ListItemCssClass
and ListItemHoverCssClass
used to beautify our dropdown list. These CSS classes are defined in the external style sheet AutoCompleteTextBox.css.
Actb.aspx.cs
Things begin to get slightly more interesting when we examine the code-behind file. The first thing to note is the using
statements at the top of the code:
. . .
using Microsoft.ApplicationBlocks.Data;
using WCPierce.Web;
using WCPierce.Web.UI.WebControls;
The Data Application Block provided by Microsoft is used to simplify some database access code. The other two using
directives give us access to the Auto Complete TextBox and CallBackHelper
classes.
Note the actbCountry_TextChanged
event that we referenced in our HTML. Everything in this function is wrapped in a try
/catch
block and any error is returned to the client by the use of CallBackHelper.HandleError
.
try
{
}
catch(Exception ex)
{
CallBackHelper.HandleError( ex );
}
This is important because errors that occur during a Call Back can be very difficult to debug (trust me on this). With this code in place and the proper JavaScript, it makes debugging errors a breeze.
The first step is to examine the contents of the txtZip
entry field. If there is a value in there, actbCountry
will default to �United States� (apologies to other countries that use Zip codes).
if( txtZip.Text.Length > 0 )
{
CallBackHelper.Write( "United States" );
}
For the next part of the code it is important to remember that this event is firing every time the user presses a key while in the ACTB
. So, if the Zip field is empty what happens is we take what the user has currently entered into the Country field, we�ll use �U� for example, and we search our database for every country that begins with the letter �U�.
AutoCompleteTextBox actb = s as AutoCompleteTextBox;
string str = String.Format("SELECT [Text]
FROM Lists
WHERE ListName='Country'
AND [Text] LIKE '{0}%'
ORDER BY [Text]", actb.Text);
All of the matching countries (Uganda, Ukraine, Uruguay, etc.) are returned in a SqlDataReader
. We then set the DataSource
property of the Auto Complete TextBox equal to the SqlDataReader
. This process is identical to that used for binding to a DropDownList
or a DataGrid
.
SqlDataReader sdr = SqlHelper.ExecuteReader(@"Server=(local);
Database=DotNetNuke;
Integrated Security=SSPI;",
CommandType.Text, str);
actb.DataSource = sdr;
actb.DataTextField = "Text";
actb.BindData();
Savvy readers will notice the only difference here is the call to BindData()
. Normally with DataBound
controls, you would call DataBind()
. Unfortunately, I wasn�t able to make this work properly. The reason is a bit complicated but you can see why if you attempt to use an Auto Complete TextBox in a DataGrid
with ViewState disabled.
That�s it! I�ve encapsulated all the hard work in the ACTB
control. You can now use it just like any other data bindable control. Surf over to this site to see a demo of the ACTB
in action. Leave the Zip blank, and enter a letter into the Country box and viola, you should see a drop down list with matches as well as the auto completion of the first country in the list. Sweet!
AutoCompleteTextBox.js
Before we start discussing this little gem, I need to recognize the great individuals who helped me make this control a reality:
The concepts from those two articles plus the CallBackObject
yielded the ACTB
. There is a lot of code in this section and rather than go over the portions covered in the two articles above, I�m only going to cover the new stuff.
AutoCompleteTextBox.prototype.TextBox_KeyUp = function(oEvent)
{
var iKeyCode = oEvent.keyCode;
if( iKeyCode == 8 )
{
this.Div.innerHTML = '';
this.Div.style.display = 'none';
return;
}
else if( iKeyCode == 16 || iKeyCode == 20 )
{
this.DoAutoSuggest = true;
}
else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) ||
(iKeyCode >= 112 && iKeyCode <= 123))
{
return;
}
else
{
this.DoAutoSuggest = true;
}
var txt = this.TextBox.value;
if( txt.length > 0 )
{
this.Cbo.DoCallBack(this.TextBox.name, txt);
}
else
{
this.Div.innerHTML = '';
this.Div.style.display = 'none';
this.Cbo.AbortCallBack();
}
}
Whenever a key is pressed in the ACTB
, this event fires. We do some checking to see what key was pressed and if there is any data in the TextBox. If so, we make the call to Cbo.DoCallBack
. As you know from Part 1, this is what initiates the server side request.
When the server side request completes, our method AutoCompleteTextBox.prototype.Cbo_Complete
is executed, and we process the results.
AutoCompleteTextBox.prototype.Cbo_Complete = function(responseText, responseXML)
{
while ( this.Div.hasChildNodes() )
this.Div.removeChild(this.Div.firstChild);
var aStr = responseText.split('\n');
var i, n = aStr.length;
if( n > 0 )
{
for ( i = 0; i < n; i++ )
{
var oDiv = document.createElement('div');
this.Div.appendChild(oDiv);
try
{
oDiv.innerHTML = aStr[i];
}
catch(e)
{
this.Cbo_Error('405','Error','Text returned from Call Back was invalid');
return;
}
oDiv.noWrap = true;
oDiv.style.width = '100%';
oDiv.className = this.ListItemClass;
oDiv.onmousedown = AutoCompleteTextBox.prototype.Div_MouseDown;
oDiv.onmouseover = AutoCompleteTextBox.prototype.Div_MouseOver;
oDiv.onmouseout = AutoCompleteTextBox.prototype.Div_MouseOut;
oDiv.AutoCompleteTextBox = this;
}
this.Div.style.display = 'block';
if( this.DoAutoSuggest == true )
this.AutoSuggest( aStr );
}
else
{
this.Div.innerHTML = '';
this.Div.style.display='none';
}
}
The data returned from the server is a new-line delimited list of country names. After we remove any current entries from the drop down list, we split the return value into an array of country names. Then, we loop through the array and add each county to the drop down list, assign some events, and set any styles. The populated list is then displayed to the user, and we call the AutoSuggest
method, to perform the type-ahead feature and put the first entry from the list in the TextBox and select the proper characters.
I apologize if I breezed over this portion but the articles referenced at the beginning of this section explain the remainder of the code.
AutoCompleteTextBox.cs
I tried to be thorough in my commenting of the control itself. We will cover the highlights of the control in lieu of taking you through line by line (plus my fingers are getting tired).
The first thing to note is that AutoComplete TextBox inherits from the built-in ASP TextBox
:
public class AutoCompleteTextBox : System.Web.UI.WebControls.TextBox
This saves us a tremendous amount of work. ACTB
has a number of additional properties used to specify some CSS information and the path to the JavaScript file to be used for the client side portion of things. The Render
method is where the action begins:
protected override void Render(HtmlTextWriter output)
{
string uId = this.UniqueID;
string newUid = uId.Replace(":", "_");
string divId = newUid + "_Div";
string jsId = newUid + "_Js";
StringBuilder acScript = new StringBuilder();
acScript.Append("<script type='\"text/javascript\"'>");
acScript.AppendFormat("var {0} = new AutoCompleteTextBox('{1}','{2}');
{0}.ListItemClass='{3}';
{0}.ListItemHoverClass='{4}';", jsId, newUid, divId,
this.ListItemCssClass, this.ListItemHoverCssClass);
acScript.Append("</script>");
Page.RegisterStartupScript(newUid, acScript.ToString());
base.Attributes.Add("AutoComplete", "False");
base.Render(output);
output.Write(String.Format("<DIV id={0}></DIV>", divId));
}
In order for the client side JavaScript to work, it needs a reference to the TextBox that will be acting as the ACTB
, as well as the <DIV>
tag that will act as the drop-down portion of the control. ASP.NET controls have an ID, which you generally use in your code, and a UniqueID
, which is a unique identifier for the control at the page level. Sometimes the ControlID
and the UniqueID
are the same, but you start running into trouble when using controls in User Controls, Server Controls, or DataList
controls. So, we grab a reference to our ACTB
UniqueID
and create ID�s for our <DIV>
tag and for use in our dynamic JavaScript code.
string uId = this.UniqueID;
string newUid = uId.Replace(":", "_");
string divId = newUid + "_Div";
string jsId = newUid + "_Js";
Next we dynamically create the JavaScript needed on the client side to create the Auto Complete TextBox. We initialize the JavaScript object with CSS properties and have ASP.NET put it in the proper place on the page for us with RegisterStartupScript
.
StringBuilder acScript = new StringBuilder();
acScript.Append("<script type='\"text/javascript\"'>");
acScript.AppendFormat("var {0} = new AutoCompleteTextBox('{1}','{2}');
{0}.ListItemClass='{3}';
{0}.ListItemHoverClass='{4}';", jsId, newUid, divId,
this.ListItemCssClass, this.ListItemHoverCssClass);
acScript.Append("</script>");
Page.RegisterStartupScript(newUid, acScript.ToString());
Finally, we have our base
class (TextBox
) render itself, then render the <DIV>
tag needed for the drop-down feature:
base.Attributes.Add("AutoComplete", "False");
base.Render(output);
output.Write(String.Format("<DIV id={0}></DIV>", divId));
We also override the OnTextChanged
event. We only want this event to fire during a CallBack and only if our control is the target of the event:
protected override void OnTextChanged(EventArgs e)
{
if( Page.Request.Params["__EVENTTARGET"] ==
this.UniqueID && CallBackHelper.IsCallBack )
{
base.OnTextChanged( e );
}
}
The last few things to address are AutoPostBack
and DataBind
. Obviously if AutoPostBack
is enabled, we defeat the purpose of AJAX. The DataBind
method had to be overridden to eliminate its functionality. If one of you figure out how to make it work, please let me know. For now, developers have to call BindData
instead.
public override void DataBind()
{
}
public override bool AutoPostBack
{
get { return false; }
}
public virtual void BindData()
{
this.OnDataBinding(EventArgs.Empty);
}
Conclusion
Finally the fruits of our labor have delivered a neat little control that brings some new functionality to the ASP.NET websites. There is a lot of code to digest and I encourage you to download the source and play around. I hope I laid the ground work and planted some ideas for future development. There is a lot of potential for this technology and it is finally getting the attention it deserves.
Listing 1 � Actb.aspx
<%@ Register TagPrefix="wcp" Namespace="WCPierce.Web.UI.WebControls"
Assembly="WCPierce.Web" %>
<%@ Page language="c#" Codebehind="Actb.aspx.cs" AutoEventWireup="false"
Inherits="TestWeb.Actb" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>AutoCompleteTextBox Example</title>
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema
content="http://schemas.microsoft.com/intellisense/ie5">
<link href="Css/AutoCompleteTextBox.css"
type="text/css" rel="stylesheet" />
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<table>
<tr>
<td colspan="2">Please enter your informaiton below.</td>
</tr>
<tr>
<td>Name:</td>
<td><asp:TextBox Runat="server" ID="txtName" /></td>
</tr>
<tr>
<td>Zip:</td>
<td><asp:TextBox Runat="server" ID="txtZip" /></td>
</tr>
<tr>
<td>Country:</td>
<td>
<wcp:AutoCompleteTextBox runat="server" id="actbCountry"
OnTextChanged="actbCountry_TextChanged"
ListItemCssClass="ListItem"
ListItemHoverCssClass="ListItemHover" />
</td>
</tr>
</table>
</form>
</body>
</HTML>
Listing 2 � Actb.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Microsoft.ApplicationBlocks.Data;
using WCPierce.Web;
using WCPierce.Web.UI.WebControls;
namespace TestWeb
{
public class Actb : System.Web.UI.Page
{
protected System.Web.UI.WebControls.TextBox txtName;
protected WCPierce.Web.UI.WebControls.AutoCompleteTextBox actbCountry;
protected System.Web.UI.WebControls.TextBox txtZip;
private void Page_Load(object sender, System.EventArgs e) { }
protected void actbCountry_TextChanged(object s, EventArgs e)
{
try
{
if( txtZip.Text.Length > 0 )
{
CallBackHelper.Write( "United States" );
}
else
{
AutoCompleteTextBox actb = s as AutoCompleteTextBox;
string str = String.Format("SELECT [Text]
FROM Lists
WHERE ListName='Country'
AND [Text] LIKE '{0}%'
ORDER BY [Text]", actb.Text);
SqlDataReader sdr = SqlHelper.ExecuteReader(@"Server=(local);
Database=DotNetNuke;
Integrated Security=SSPI;",
CommandType.Text, str);
actb.DataSource = sdr;
actb.DataTextField = "Text";
actb.BindData();
}
}
catch(Exception ex)
{
CallBackHelper.HandleError( ex );
}
}
#region Web Form Designer generated code
}
}
Listing 3 � AutoCompleteTextBox.js
function AutoCompleteTextBox(TextBoxId, DivId, DivClass)
{
var oThis = this;
var oText = document.getElementById(TextBoxId);
var oDiv = document.getElementById(DivId);
this.TextBox = oText;
this.Div = oDiv;
this.Cbo = new CallBackObject();
this.Cbo.OnComplete = function(responseText,responseXML)
{oThis.Cbo_Complete(responseText,responseXML);};
this.Cbo.OnError = function(status,statusText,responseText)
{oThis.Cbo_Error(status,statusText,responseText);};
oText.AutoCompleteTextBox = this;
oText.onkeyup = AutoCompleteTextBox.prototype.OnKeyUp;
oText.onblur = AutoCompleteTextBox.prototype.OnBlur;
var c = GetCoords(oText);
var n = oText.style.pixelHeight;
if( !n )
{
n = 25;
}
else
{
n += 2;
}
oDiv.style.left = c.x;
oDiv.style.top = c.y + n;
oDiv.style.display = 'none';
oDiv.style.position = 'absolute';
if( DivClass )
oDiv.className = DivClass;
else
{
oDiv.style.border = '1';
oDiv.style.borderColor = 'black';
oDiv.style.borderStyle = 'solid';
oDiv.style.backgroundColor = 'white';
oDiv.style.padding = '2';
}
}
AutoCompleteTextBox.prototype.DoAutoSuggest = false;
AutoCompleteTextBox.prototype.ListItemClass = '';
AutoCompleteTextBox.prototype.ListItemHoverClass = '';
AutoCompleteTextBox.prototype.OnBlur = function()
{
this.AutoCompleteTextBox.TextBox_Blur();
}
AutoCompleteTextBox.prototype.TextBox_Blur = function()
{
this.Div.style.display='none';
}
AutoCompleteTextBox.prototype.OnKeyUp = function(oEvent)
{
if (!oEvent)
{
oEvent = window.event;
}
this.AutoCompleteTextBox.TextBox_KeyUp(oEvent);
}
AutoCompleteTextBox.prototype.TextBox_KeyUp = function(oEvent)
{
var iKeyCode = oEvent.keyCode;
if( iKeyCode == 8 )
{
this.Div.innerHTML = '';
this.Div.style.display = 'none';
return;
}
else if( iKeyCode == 16 || iKeyCode == 20 )
{
this.DoAutoSuggest = true;
}
else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) ||
(iKeyCode >= 112 && iKeyCode <= 123))
{
return;
}
else
{
this.DoAutoSuggest = true;
}
var txt = this.TextBox.value;
if( txt.length > 0 )
{
this.Cbo.DoCallBack(this.TextBox.name, txt);
}
else
{
this.Div.innerHTML = '';
this.Div.style.display = 'none';
this.Cbo.AbortCallBack();
}
}
AutoCompleteTextBox.prototype.Cbo_Complete =
function(responseText, responseXML)
{
while ( this.Div.hasChildNodes() )
this.Div.removeChild(this.Div.firstChild);
var aStr = responseText.split('\n');
var i, n = aStr.length;
if( n > 0 )
{
for ( i = 0; i < n; i++ )
{
var oDiv = document.createElement('div');
this.Div.appendChild(oDiv);
try
{
oDiv.innerHTML = aStr[i];
}
catch(e)
{
this.Cbo_Error('405','Error',
'Text returned from Call Back was invalid');
return;
}
oDiv.noWrap = true;
oDiv.style.width = '100%';
oDiv.className = this.ListItemClass;
oDiv.onmousedown = AutoCompleteTextBox.prototype.Div_MouseDown;
oDiv.onmouseover = AutoCompleteTextBox.prototype.Div_MouseOver;
oDiv.onmouseout = AutoCompleteTextBox.prototype.Div_MouseOut;
oDiv.AutoCompleteTextBox = this;
}
this.Div.style.display = 'block';
if( this.DoAutoSuggest == true )
this.AutoSuggest( aStr );
}
else
{
this.Div.innerHTML = '';
this.Div.style.display='none';
}
}
AutoCompleteTextBox.prototype.Cbo_Error =
function(status, statusText, responseText)
{
alert('CallBackObject Error: status=' + status + '\nstatusText=' +
statusText + '\n' + responseText);
}
AutoCompleteTextBox.prototype.Div_MouseDown = function()
{
this.AutoCompleteTextBox.TextBox.value = this.innerHTML;
}
AutoCompleteTextBox.prototype.Div_MouseOver = function()
{
if( this.AutoCompleteTextBox.ListItemHoverClass.length > 0 )
this.className = this.AutoCompleteTextBox.ListItemHoverClass;
else
{
this.style.backgroundColor = 'black';
this.style.color = 'white';
}
}
AutoCompleteTextBox.prototype.Div_MouseOut = function()
{
if( this.AutoCompleteTextBox.ListItemClass.length > 0 )
this.className = this.AutoCompleteTextBox.ListItemClass;
else
{
this.style.backgroundColor = 'white';
this.style.color = 'black';
}
}
AutoCompleteTextBox.prototype.AutoSuggest =
function(aSuggestions )
{
if (aSuggestions.length > 0)
{
this.TypeAhead(aSuggestions[0]);
}
}
AutoCompleteTextBox.prototype.TypeAhead =
function( sSuggestion )
{
if( this.TextBox.createTextRange || this.TextBox.setSelectionRange)
{
var iLen = this.TextBox.value.length;
this.TextBox.value = sSuggestion;
this.SelectRange(iLen, sSuggestion.length);
}
}
AutoCompleteTextBox.prototype.SelectRange =
function (iStart , iLength )
{
if (this.TextBox.createTextRange)
{
var oRange = this.TextBox.createTextRange();
oRange.moveStart("character", iStart);
oRange.moveEnd("character", iLength - this.TextBox.value.length);
oRange.select();
}
else if (this.TextBox.setSelectionRange)
{
this.TextBox.setSelectionRange(iStart, iLength);
}
this.TextBox.focus();
}
function GetCoords(obj )
{
var newObj = new Object();
newObj.x = obj.offsetLeft;
newObj.y = obj.offsetTop;
theParent = obj.offsetParent;
while(theParent != null)
{
newObj.y += theParent.offsetTop;
newObj.x += theParent.offsetLeft;
theParent = theParent.offsetParent;
}
return newObj;
}
Listing 4 � AutoCompleteTextBox.cs
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using WCPierce.Web.UI;
[assembly:TagPrefix("WCPierce.Web.UI.WebControls", "wcp")]
namespace WCPierce.Web.UI.WebControls
{
[DefaultProperty("Text"),
ToolboxData("<{0}:AutoCompleteTextBox runat="server">")]
public class AutoCompleteTextBox : System.Web.UI.WebControls.TextBox
{
#region Member Variables
private string _scriptPath = string.Empty;
private object _dataSource = null;
private static readonly string _FormatString = "{0}\n";
private static readonly string _ScriptPath =
"AutoCompleteTextBox.ScriptPath";
private string _listItemCssClass = string.Empty;
private string _listItemHoverCssClass = string.Empty;
#endregion
#region Public Properties
public string ScriptPath
{
get
{
if( _scriptPath != string.Empty )
return ResolveUrl(_scriptPath);
try
{
return
ResolveUrl(System.Configuration.ConfigurationSettings.AppSettings[
AutoCompleteTextBox._ScriptPath]);
}
catch
{
return null;
}
}
set { _scriptPath = value; }
}
public string ListItemCssClass
{
get { return _listItemCssClass; }
set { _listItemCssClass = value; }
}
public string ListItemHoverCssClass
{
get { return _listItemHoverCssClass; }
set { _listItemHoverCssClass = value; }
}
true),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
DefaultValue((string) null)>
public virtual object DataSource
{
get
{
return _dataSource;
}
set
{
if (((value != null) && !(value is IListSource)) &&
!(value is IEnumerable))
{
throw new ArgumentException("Invalid_DataSource_Type: " +
this.ID);
}
_dataSource = value;
}
}
public virtual string DataTextField
{
get
{
object o = this.ViewState["DataTextField"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataTextField"] = value;
}
}
public virtual string DataTextFormatString
{
get
{
object o = this.ViewState["DataTextFormatString"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataTextFormatString"] = value;
}
}
[DefaultValue("")]
public virtual string DataMember
{
get
{
object o = this.ViewState["DataMember"];
if (o != null)
{
return (string)o;
}
return string.Empty;
}
set
{
this.ViewState["DataMember"] = value;
}
}
#endregion
#region Overrides
protected override void Render(HtmlTextWriter output)
{
string uId = this.UniqueID;
string newUid = uId.Replace(":", "_");
string divId = newUid + "_Div";
string jsId = newUid + "_Js";
StringBuilder acScript = new StringBuilder();
acScript.Append("<script type='\"text/javascript\"'>");
acScript.AppendFormat("var {0} =
new AutoCompleteTextBox('{1}','{2}');
{0}.ListItemClass='{3}';
{0}.ListItemHoverClass='{4}';", jsId, newUid, divId,
this.ListItemCssClass, this.ListItemHoverCssClass);
acScript.Append("</script>");
Page.RegisterStartupScript(newUid, acScript.ToString());
base.Attributes.Add("AutoComplete", "False");
base.Render(output);
output.Write(String.Format("<DIV id={0}></DIV>", divId));
}
protected override void OnPreRender(EventArgs e)
{
this._RegisterCommonScripts();
base.OnPreRender(e);
}
protected override void OnTextChanged(EventArgs e)
{
if( Page.Request.Params["__EVENTTARGET"] ==
this.UniqueID && CallBackHelper.IsCallBack )
{
base.OnTextChanged( e );
}
}
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e);
IEnumerable ie =
DataSourceHelper.GetResolvedDataSource(this.DataSource,
this.DataMember);
StringBuilder sb = new StringBuilder();
if( ie != null )
{
bool useTextField = false;
bool useFormatString = false;
string textField = DataTextField;
string formatString = DataTextFormatString;
if( textField.Length != 0 )
{
useTextField = true;
}
if( formatString.Length != 0 )
{
useFormatString = true;
}
foreach( object o in ie )
{
if( useTextField )
{
if( textField.Length > 0)
{
sb.AppendFormat(AutoCompleteTextBox._FormatString,
DataBinder.GetPropertyValue(o, textField, formatString));
}
}
else
{
if( useFormatString )
{
sb.AppendFormat( AutoCompleteTextBox._FormatString,
string.Format(formatString, o) );
}
else
{
sb.AppendFormat(AutoCompleteTextBox._FormatString,
o.ToString());
}
}
}
}
if( sb.Length > 1 )
sb.Remove(sb.Length-1, 1);
CallBackHelper.Write( sb.ToString() );
}
public override void DataBind()
{
}
public override bool AutoPostBack
{
get { return false; }
}
#endregion
#region Public Methods
public virtual void BindData()
{
this.OnDataBinding(EventArgs.Empty);
}
#endregion
#region Helper Methods
private void _RegisterCommonScripts()
{
if (!this.Page.IsClientScriptBlockRegistered("AutoCompleteTextBox"))
{
StringBuilder script = new StringBuilder();
script.AppendFormat("<script src="{0}" type=text/javascript></script>",
this.ScriptPath);
this.Page.RegisterClientScriptBlock("AutoCompleteTextBox",
script.ToString());
}
}
#endregion
}
}
History