// ********************************************************
// 自动完成控件
// Designed by Faib Studio.
// Copyright 2007
// Email faib920@126.com or QQ 55570729
// ********************************************************

using System;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Design;

namespace FaibClass.WebControls


{
[DefaultProperty("Text")]
[ParseChildren(true, "Items")]
[DefaultEvent("Changed")]
[ToolboxData("<{0}:AutoCompleteTextBox runat=server></{0}:AutoCompleteTextBox>")]
public class AutoCompleteTextBox : BaseTextBox, IPostBackDataHandler, IPostBackEventHandler

{
[Description("列表项改变时。")]
public event EventHandler Changed;


私有变量#region 私有变量
private ListItemCollection m_lics;
private int m_Rows = 8;
private bool m_SendKeyTab = false;
private bool m_MatchAnywhere = true;
private bool m_ListAllItemOnFocus = true;
private ListItem m_SelectedItem = null;
private int m_SelectedIndex = -1;
#endregion

public AutoCompleteTextBox()

{
m_lics = new ListItemCollection();
}


属性#region 属性
[Category("Appearance")]
[Description("文本值。")]
[PersistenceMode(PersistenceMode.Attribute)]
public override string Text

{

get
{return base.Text;}

set
{base.Text = value;}
}

[Category("Data")]
[Description("列表中项的集合。")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[NotifyParentProperty(true)]
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public ListItemCollection Items

{

get
{return m_lics;}
}

[Category("Appearance")]
[Description("列表显示的行数。")]
[DefaultValue(8)]
public new int Rows

{

get
{return m_Rows;}

set
{m_Rows = value;}
}

[Category("Behavior")]
[Description("按回车选择时是否发移到下一控件。")]
[DefaultValue(false)]
public bool SendKeyTab

{

get
{return m_SendKeyTab;}

set
{m_SendKeyTab = value;}
}

[Category("Behavior")]
[Description("是否匹配任意位置的字符串。")]
[DefaultValue(true)]
public bool MatchAnywhere

{

get
{return m_MatchAnywhere;}

set
{m_MatchAnywhere = value;}
}
[Category("Behavior")]
[Description("是否在获得焦点时列出所有项。")]
[DefaultValue(true)]
public bool ListAllItemOnFocus

{

get
{return m_ListAllItemOnFocus;}

set
{m_ListAllItemOnFocus = value;}
}

[Browsable(false)]
[Description("返回被选中项。")]
public ListItem SelectedItem

{

get
{return m_SelectedItem;}
}

[Browsable(false)]
[Description("返回被选中项的索引。")]
[DefaultValue(-1)]
public int SelectedIndex

{

get
{return m_SelectedIndex;}

set
{
m_SelectedIndex = value;
m_SelectedItem = m_lics[m_SelectedIndex];
this.Text = m_SelectedItem.Text;
}
}

internal new TextBoxMode TextMode

{

get
{return TextBoxMode.SingleLine;}
}

internal new bool Wrap

{

get
{return false;}
}

internal new int MaxLength

{

get
{return 0;}
}

internal new int Columns

{

get
{return 0;}
}
#endregion


公有方法#region 公有方法
protected override void OnInit(EventArgs e)

{
if (Page != null)

{
//将控件注册为需要回发处理的控件
Page.RegisterRequiresPostBack(this);
}
base.OnInit (e);
}

protected override void OnPreRender(EventArgs e)

{
base.OnPreRender (e);
if (Page != null)

{
//注册隐藏字段,用于保存客户端选择的索引及对Items的修改
Page.RegisterHiddenField("__" + this.ClientID + "_State__", m_SelectedIndex.ToString() + "※");
}
}

protected override void OnLoad(EventArgs e)

{
//注册一个js文件到缓存中
Util.RegisterCacheFile(Page, "AutoCompleteTextBox", "autocompleteTextbox.fbs.ashx", "Resource.AutoCompleteTextBox.js");
//输出控件初始化脚本
Util.RegisterStartupScript(Page, "AutoCompleteTextBox_" + this.ClientID, "\n<script>\nif(document.getElementById(\"" + this.ClientID + "\")){AutoCompleteBox_Init(document.getElementById(\"" + this.ClientID + "\"));}\n</script>");
//添加属性
Attributes.Add("rows", m_Rows.ToString());
if(AutoPostBack)Attributes.Add("autopostback", "1");
if(m_MatchAnywhere)Attributes.Add("matchanywhere", "1");
if(m_ListAllItemOnFocus)Attributes.Add("listallitemonfocus", "1");
if(m_SendKeyTab)Attributes.Add("sendkeytab", "1");

base.OnLoad (e);
}

protected override void Render(HtmlTextWriter output)

{
base.Render (output);
if(IsDesignMode)return;
//输出Items中的每一项生成fsc:ListItem标签
output.Write("<?XML:NAMESPACE PREFIX=\"fsc\" />");
output.AddAttribute("id", this.ClientID + "_items");
output.RenderBeginTag("fsc:AutoCompleteTextBox");
foreach(ListItem lt in m_lics)

{
output.WriteBeginTag("fsc:ListItem");
output.WriteAttribute("value", lt.Value);
output.WriteAttribute("text", lt.Text);
output.WriteLine(" />");
}
output.RenderEndTag();
}

[DescriptionAttribute("引发 PageIndexChanged 事件。")]
protected virtual void OnChanged()

{
if(Changed != null)

{
Changed(this, new EventArgs());
}
}

protected override void LoadViewState(object savedState)

{
base.LoadViewState (savedState);
//将Items保存到ViewState,由于ListItem不支持序列化,所以需要使用自定义类来进行保存
if (ViewState["Items"] != null)
m_lics = Util.LoadListItemFromViewState(ViewState["Items"]);
}

protected override object SaveViewState()

{
//从ViewState中读取Items
ViewState["Items"] = Util.SaveListItemToViewState(m_lics);
return base.SaveViewState ();
}
#endregion


接口#region 接口
void IPostBackDataHandler.RaisePostDataChangedEvent()

{
}

void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)

{
//触发事件
if(eventArgument == "")
OnChanged();
}

bool IPostBackDataHandler.LoadPostData(String postDataKey, NameValueCollection values)

{
//处理回发数据
string value = values["__" + this.ClientID + "_State__"];
if (value != null)

{
string[] data = value.Split('※');//客户端Items的修改,使用saveState后生成的数据
if(data[1] != "")

{
m_lics.Clear();
for(int i = 1; i < data.Length; i++)

{
string[] da1 = data[i].Split('□');
m_lics.Add(new ListItem(da1[0], da1[1]));
}
SaveViewState();
}
if(values[this.ClientID].Length == 0)

{
m_SelectedIndex = -1;
m_SelectedItem = null;
}
else

{
if(data[0].CompareTo("-1") != 0)//选定项

{
try

{
m_SelectedIndex = int.Parse(data[0]);
m_SelectedItem = this.Items[m_SelectedIndex];
}
catch

{
throw;
}
}
}
}
this.Text = values[this.ClientID];
return false;
}
#endregion
}
}

此控件继承自BaseTextBox类,BaseTextBox是一个简单的类,目前只实现了一个属性:
protected internal virtual bool IsDesignMode

{

get
{ return (Site != null) ? Site.DesignMode : false; }
}
Javascript源代码见
http://www.cnblogs.com/faib/archive/2007/05/10/741875.html