这个主要实现的是一个自动完成的功能,C#本身提供的自动完成效果并不好,所以自己造了一个轮子。。。。
实现这个功能依靠于两个控件一个是ListBox,一个是ComboBox。ComboBox是在类外定义的,就是进行模糊查询时输入值的控件,该控件的Dropdownstyle要设置为Simple。实现的主要原理是输入值后触发相应事件,生成一个ListBox控件,并进行查询,将查询结果填入ListBox控件,通过计算ComboBox控件的位置,将ListBox控件挂到ComboBox的下方。通过函数重载在查询时提供两种方法,一种是支持Sqlsever相关查询语句的内部查询方法,该方法在类内实现,需要提供相关的Sql查询方法,这里使用的是Sql的存储过程,可能要略作修改。还有一种是外部查询,在类外写相关的查询方法,并通过委托传入类内。另外提供了两个外部方法,用于选定查询结果触发后续事件,依然是通过委托传入类内。
代码如下:
<p align="left"><span style="color:blue;">using</span> System;</p><p align="left"><span style="color:blue;">using</span> System.Collections.Generic;</p><p align="left"><span style="color:blue;">using</span> System.Data;</p><p align="left"><span style="color:blue;">using</span> System.Drawing;</p><p align="left"><span style="color:blue;">using</span> System.Linq;</p><p align="left"><span style="color:blue;">using</span> System.Runtime.InteropServices;</p><p align="left"><span style="color:blue;">using</span> System.Text;</p><p align="left"><span style="color:blue;">using</span> System.Threading.Tasks;</p><p align="left"><span style="color:blue;">using</span> System.Windows.Forms;</p><p align="left"><span style="color:blue;">using</span> TNB.pubClass;</p>
/*
C#实现自动完成功能
2016年10月14日
*/
namespace TNB
{
class AutoComplete
{
public delegate List<string> External_Query(); //外部查询方法委托(返回值为List<string>,无参数)
External_Query ExternalQuery; //外部查询方法
public delegate void External_del(); //外部事件处理方法委托(返回值为空,无参数)
External_del REvent_Strike; //外部事件处理方法(触发)
External_del REvent_Reset; //外部事件处理方法(恢复)
ComboBox rec_Com; //ComboBox控件
ListBox rec_List; //ListBox控件
string search_value=""; //输入查询值
bool ExtQueryFlag = false; //是否使用外部查询标志
//数据库相关参数字段
string Table; //数据库表名
string Columns; //数据库列
string PYixcol; //数据库拼音缩写列
string Nameixcol; //数据库名称列
string Col1; //数据库序号列
int Retindex; //数据库查询结果列列号(0开始)
int MaxItems; //最大显示条数
public AutoComplete() { }
//绑定控件、外部事件处理方法
//参数说明 1.Com:查询是输入值得ComboBox控件 2.Gb(P) 控件所在父容器 3. Fun_Strike :选定查询结果的后续触发事件方法
//4.Fun_Rest :删除查询结果触发事件的恢复方法 5.X:ComboBox控件X坐标 6.Y:ComboBox控件Y坐标
//不提供ComboBox在屏幕中的位置坐标,并且父窗体为GroupBox
public void Binding_Extern(ComboBox Com, GroupBox Gb, External_del Fun_Strike=null, External_del Fun_Reset=null)
{
//绑定方法
if (Fun_Strike != null)
REvent_Strike = Fun_Strike; //绑定外部事件处理方法(触发)
else
REvent_Strike = EmptyFun; //绑定空方法
if(Fun_Reset!=null)
REvent_Reset = Fun_Reset; //绑定外部事件处理方法(恢复)
else
REvent_Reset = EmptyFun; //绑定空方法
rec_Com = Com; //绑定外部ComboBox控件
//动态创建ListBox控件
rec_List = new ListBox(); //实例化控件
int LocationX = Com.Location.X; //控件位置X
int LocationY = Com.Location.Y + 19; //控件位置Y
int Width = Com.Size.Width; //控件大小
rec_List.Location = new Point(LocationX, LocationY); //设置控件位置
rec_List.Size = new Size(Width, 0); //设置控件大小
rec_List.Visible = false; //控件初始化为隐藏
Gb.Controls.Add(rec_List); //添加到所在GroupBox
rec_List.BringToFront(); //设置控件最上层显示
//注册控件事件
rec_List.Click += new System.EventHandler(ListBox_Click); //ListBox的单击事件
rec_List.SelectedIndexChanged += new System.EventHandler(ListBox_SelectedIndexChanged); //ListBox的选中值改变事件
rec_Com.Click += new System.EventHandler(ComboBox_Click); //ComboBox的单击事件
rec_Com.KeyDown += new System.Windows.Forms.KeyEventHandler(ComboBox_KeyDown); //ComboBox的按键事件
rec_Com.TextUpdate += new System.EventHandler(ComboBox_TextUpdate); //ComboBox的文本更新事件
rec_Com.TextChanged += new System.EventHandler(ComboBox_TextChanged); //ComboBox的文本改变事件
}
//提供ComboBox在屏幕中的位置坐标,并且父窗体为GroupBox
public void Binding_Extern(ComboBox Com, GroupBox Gb, int X,int Y,External_del Fun_Strike = null, External_del Fun_Reset = null)
{
//绑定方法
if (Fun_Strike != null)
REvent_Strike = Fun_Strike; //绑定外部事件处理方法(触发)
else
REvent_Strike = EmptyFun; //绑定空方法
if (Fun_Reset != null)
REvent_Reset = Fun_Reset; //绑定外部事件处理方法(恢复)
else
REvent_Reset = EmptyFun; //绑定空方法
rec_Com = Com; //绑定外部ComboBox控件
//动态创建ListBox控件
rec_List = new ListBox(); //实例化控件
int LocationX = X; //控件位置X
int LocationY = Y; //控件位置Y
int Width = Com.Size.Width; //控件大小
rec_List.Location = new Point(LocationX, LocationY); //设置控件位置
rec_List.Size = new Size(Width, 0); //设置控件大小
rec_List.Visible = false; //控件初始化为隐藏
Gb.Controls.Add(rec_List); //添加到所在GroupBox
rec_List.BringToFront(); //设置控件最上层显示
//注册控件事件
rec_List.Click += new System.EventHandler(ListBox_Click); //ListBox的单击事件
rec_List.SelectedIndexChanged += new System.EventHandler(ListBox_SelectedIndexChanged); //ListBox的选中值改变事件
rec_Com.Click += new System.EventHandler(ComboBox_Click); //ComboBox的单击事件
rec_Com.KeyDown += new System.Windows.Forms.KeyEventHandler(ComboBox_KeyDown); //ComboBox的按键事件
rec_Com.TextUpdate += new System.EventHandler(ComboBox_TextUpdate); //ComboBox的文本更新事件
rec_Com.TextChanged += new System.EventHandler(ComboBox_TextChanged); //ComboBox的文本改变事件
}
//不提供ComboBox在屏幕中的位置坐标,并且父窗体为Panel
public void Binding_Extern(ComboBox Com, Panel P, External_del Fun_Strike = null, External_del Fun_Reset = null)
{
//绑定方法
if (Fun_Strike != null)
REvent_Strike = Fun_Strike; //绑定外部事件处理方法(触发)
else
REvent_Strike = EmptyFun; //绑定空方法
if (Fun_Reset != null)
REvent_Reset = Fun_Reset; //绑定外部事件处理方法(恢复)
else
REvent_Reset = EmptyFun; //绑定空方法
rec_Com = Com; //绑定外部ComboBox控件
//动态创建ListBox控件
rec_List = new ListBox(); //实例化控件
int LocationX = Com.Location.X; //控件位置X
int LocationY = Com.Location.Y + 19; //控件位置Y
int Width = Com.Size.Width; //控件大小
rec_List.Location = new Point(LocationX, LocationY); //设置控件位置
rec_List.Size = new Size(Width, 0); //设置控件大小
rec_List.Visible = false; //控件初始化为隐藏
P.Controls.Add(rec_List); //添加到所在GroupBox
rec_List.BringToFront(); //设置控件最上层显示
//注册控件事件
rec_List.Click += new System.EventHandler(ListBox_Click); //ListBox的单击事件
rec_List.SelectedIndexChanged += new System.EventHandler(ListBox_SelectedIndexChanged); //ListBox的选中值改变事件
rec_Com.Click += new System.EventHandler(ComboBox_Click); //ComboBox的单击事件
rec_Com.KeyDown += new System.Windows.Forms.KeyEventHandler(ComboBox_KeyDown); //ComboBox的按键事件
rec_Com.TextUpdate += new System.EventHandler(ComboBox_TextUpdate); //ComboBox的文本更新事件
rec_Com.TextChanged += new System.EventHandler(ComboBox_TextChanged); //ComboBox的文本改变事件
}
//提供ComboBox在屏幕中的位置坐标,并且父窗体为Panel
public void Binding_Extern(ComboBox Com, Panel P, int X, int Y, External_del Fun_Strike = null, External_del Fun_Reset = null)
{
//绑定方法
if (Fun_Strike != null)
REvent_Strike = Fun_Strike; //绑定外部事件处理方法(触发)
else
REvent_Strike = EmptyFun; //绑定空方法
if (Fun_Reset != null)
REvent_Reset = Fun_Reset; //绑定外部事件处理方法(恢复)
else
REvent_Reset = EmptyFun; //绑定空方法
rec_Com = Com; //绑定外部ComboBox控件
//动态创建ListBox控件
rec_List = new ListBox(); //实例化控件
int LocationX = X; //控件位置X
int LocationY = Y; //控件位置Y
int Width = Com.Size.Width; //控件大小
rec_List.Location = new Point(LocationX, LocationY); //设置控件位置
rec_List.Size = new Size(Width, 0); //设置控件大小
rec_List.Visible = false; //控件初始化为隐藏
P.Controls.Add(rec_List); //添加到所在GroupBox
rec_List.BringToFront(); //设置控件最上层显示
//注册控件事件
rec_List.Click += new System.EventHandler(ListBox_Click); //ListBox的单击事件
rec_List.SelectedIndexChanged += new System.EventHandler(ListBox_SelectedIndexChanged); //ListBox的选中值改变事件
rec_Com.Click += new System.EventHandler(ComboBox_Click); //ComboBox的单击事件
rec_Com.KeyDown += new System.Windows.Forms.KeyEventHandler(ComboBox_KeyDown); //ComboBox的按键事件
rec_Com.TextUpdate += new System.EventHandler(ComboBox_TextUpdate); //ComboBox的文本更新事件
rec_Com.TextChanged += new System.EventHandler(ComboBox_TextChanged); //ComboBox的文本改变事件
}
//绑定查询
//绑定内部查询,使用SqlSever数据库查询
//参数说明:1.table:表名 2.columns:表中所有列 3.pyixcol:拼音首字母索引列 4.nameixcol:中文名称索引列 5.col1:排序列
//6.Retindex:查询内容列索引(0开始) 7.maxItems: ListBox控件显示的最大条目
public void Binding_DataBase(string table, string columns, string pyixcol, string nameixcol, string col1, int retindex,int maxItems)
{
Table = table;
Columns = columns;
PYixcol = pyixcol;
Nameixcol = nameixcol;
Col1 = col1;
Retindex = retindex;
MaxItems = maxItems;
}
//绑定外部查询,提供外部查询方法
//参数说明 1.ExtQuery:外部查询方法函数委托 2.maxItems: ListBox控件显示的最大条目
public void Binding_DataBase(External_Query ExtQuery,int maxItems)
{
ExternalQuery = ExtQuery;
MaxItems = maxItems;
ExtQueryFlag = true;
}
//内部查询方法
private void Query1()
{
//符合查询条件
if (rec_Com.Text.Trim() != "" && rec_Com.Text.Trim() != "%")
{
//模糊查询部分
HashSet<string> Hset = new HashSet<string>(); //初始化查询hash结果集合
string STR = ""; //查询内容初始化
string logo = ""; //查询串初始化
bool flag = false; //查询标志初始化 (查询成功为true)
//1.字符串匹配查询
STR = rec_Com.Text.Trim(); //获取查询内容
#region 字符串匹配查询
logo = Nameixcol + " LIKE" + "'" + STR + "%" + "'";
DataSet J1 = new DataSet();
try
{
int c1 = 0;
DataBaseAccess.GetPageRecord(1000, 1, Columns, Table, logo, Col1, false, "", ref c1, ref J1);
}
catch (Exception Error)
{
MessageBox.Show(Error.Message.ToString());
}
//字符串匹配查询结果装填到hash结果集
if (J1.Tables[0].Rows.Count > 0)
{
for (int k = 0; k < J1.Tables[0].Rows.Count; ++k)
Hset.Add(J1.Tables[0].Rows[k][Retindex].ToString());
flag = true; //改变查询结果标志
}
#endregion
//2.字符串拼音首字母查询
STR = ConvertToFirstSpell(STR); //将查询内容转换为拼音首字母组成的字符串
#region 拼音首字母查询
logo = PYixcol + " LIKE" + "'" + STR + "%" + "'";
DataSet J2 = new DataSet();
try
{
int c1 = 0;
DataBaseAccess.GetPageRecord(1000, 1, Columns, Table, logo, Col1, false, "", ref c1, ref J2);
}
catch (Exception Error)
{
MessageBox.Show(Error.Message.ToString());
}
//字符串拼音首字母查询结果装填到hash结果集
if (J2.Tables[0].Rows.Count > 0)
{
for (int k = 0; k < J2.Tables[0].Rows.Count; ++k) //装填items
Hset.Add(J2.Tables[0].Rows[k][Retindex].ToString());
flag = true;
}
#endregion
//hash结果集处理部分
#region hash结果集非空
if (flag)
{
int cnt = Hset.Count; //获取hash结果集条数
//设置Listbox控件的大小
int ListBoxSize = Convert.ToInt32(rec_List.Font.Size * 1.5);
if (cnt > MaxItems)
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, 15 * MaxItems);
else if (cnt == 1)
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, ListBoxSize + 5);
else
{
int weight = ListBoxSize * cnt; //计算listbox高度
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, weight);
}
//装填集合
rec_List.Items.Clear(); //清空Listbox原来内容
rec_List.Items.AddRange(Hset.ToArray()); //将hash结果集装入Listbox
rec_List.Visible = true; //显示下拉
search_value = rec_Com.Text; //获取初始查询之
}
#endregion
#region hash结果集为空
else
{
rec_List.Visible = false; //关闭下拉
rec_List.Items.Clear(); //清空条目
}
#endregion
}
//不符合查询条件
else
{
rec_List.Visible = false; //关闭下拉
rec_List.Items.Clear(); //清空
}
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
}
//外部查询方法
private void Query2()
{
//符合查询要求
if (rec_Com.Text.Trim() != "")
{
List<string> list = ExternalQuery();
if (list.Count != 0)
{
#region 传入结果集非空处理方法
int cnt = list.Count; //获取结果集条数
//设置Listbox控件的大小
int ListBoxSize = Convert.ToInt32(rec_List.Font.Size * 1.5);
if (cnt > MaxItems)
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, 15 * MaxItems);
else if (cnt == 1)
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, ListBoxSize + 5);
else
{
int weight = ListBoxSize * cnt; //计算listbox高度
rec_List.SetBounds(rec_List.Location.X, rec_List.Location.Y, rec_List.Width, weight);
}
//装填集合
rec_List.Items.Clear(); //清空Listbox原来内容
rec_List.Items.AddRange(list.ToArray()); //将hash结果集装入Listbox
rec_List.Visible = true; //显示下拉
search_value = rec_Com.Text; //获取初始查询之
#endregion
}
else //结果集为空处理方法
{
rec_List.Visible = false; //关闭下拉
rec_List.Items.Clear(); //清空条目
}
}
else //不符合查询要求
{
rec_List.Visible = false; //关闭下拉
rec_List.Items.Clear(); //清空条目
}
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
}
//事件定义
//ListBox的单击事件(实现鼠标单击选择)
private void ListBox_Click(object sender, EventArgs e)
{
string Selvalue; //选中值
if ((Selvalue = rec_List.SelectedItem.ToString()) != "") //存在选中内容
{
rec_Com.Text = Selvalue; //将选中值在ComboBox中显示
rec_Com.Focus(); //设置焦点到ComboBox
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
search_value = rec_Com.Text; //更新输入查询
rec_List.Visible = false; //关闭下拉
}
}
//ListBox的选中改变事件 (实现ComboBox中文本的改变)
private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (rec_List.SelectedIndex == -1)
{
rec_Com.Text = search_value; //更新输入查询值
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
}
else
{
rec_Com.Text = rec_List.SelectedItem.ToString(); //改变ComboBox内容
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
}
}
//ComboBox的单击事件 (实现单击显示下拉窗体)
private void ComboBox_Click(object sender, EventArgs e)
{
if (rec_Com.Text != "" && rec_List.Items.Count != 0) //如果存在查询内容,并且有查询结果
rec_List.Visible = true; //打开下拉
}
//ComboBox的按键事件 (实现键盘上下键移动选中选项和回车确认选中项)
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
//下一项操作
if (e.KeyCode == Keys.Down && rec_List.Visible == true)
{
rec_List.SelectedIndex++; //向下移动选中项
}
//上一项操作
else if (e.KeyCode == Keys.Up && rec_List.Visible == true)
{
rec_List.SelectedIndex--; //向上移动选中项
}
//确认选中项
else if (e.KeyCode == Keys.Enter)
{
rec_List.Visible = false; //关闭下拉
search_value = rec_Com.Text; //更新输入值
rec_Com.SelectionStart = rec_Com.Text.Length; //设置光标位置到文字结尾
}
}
//ComboBox的文本更新事件 (实现输入文本后进行的模糊查询)
private void ComboBox_TextUpdate(object sender, EventArgs e)
{
if (ExtQueryFlag)
{
Query2();
}
else
{
Query1();
}
}
//ComboBox的文本改变事件 (实现文本改变后的调用外部事件处理方法)
private void ComboBox_TextChanged(object sender, EventArgs e)
{
//如果没有查询项(那么一定不符合要求)
if (rec_List.Items.Count == 0)
{
REvent_Reset(); //调用外部关联事件处理方法(恢复)
}
//存在查询项(判断确认的文本是否符合要求)
else
{
bool flag = false; //是否符合要求的标志
for (int i = 0; i < rec_List.Items.Count; ++i)
{
if (rec_Com.Text == rec_List.Items[i].ToString()) //符合要求
{
flag = true;
break;
}
}
if(flag)
REvent_Strike(); //调用外部关联事件处理方法(触发)
else
REvent_Reset(); //调用外部关联事件处理方法(恢复)
}
}
//汉字拼音转换(用于模糊查询)
public string ConvertToFirstSpell(string strChinese)
{
int len = strChinese.Length;
string myStr = "";
for (int i = 0; i < len; i++)
{
myStr += GetFirstSpell(strChinese.Substring(i, 1));
}
return myStr;
}
//获取第一个汉字的首字母(大写)(用于模糊查询)
public string GetFirstSpell(string charChinese)
{
byte[] arrCN = Encoding.Default.GetBytes(charChinese);
if (arrCN.Length > 1)
{
int area = (short)arrCN[0];
int pos = (short)arrCN[1];
int code = (area << 8) + pos;
int[] areacode = { 45217, 45253, 45761, 46318, 46826, 47010, 47297, 47614, 48119, 48119, 49062, 49324, 49896, 50371, 50614, 50622, 50906, 51387, 51446, 52218, 52698, 52698, 52698, 52980, 53689, 54481 };
for (int i = 0; i < 26; i++)
{
int max = 55290;
if (i != 25) max = areacode[i + 1];
if (areacode[i] <= code && code < max)
{
byte[] bytes = new byte[] { (byte)(65 + i) };
return Encoding.Default.GetString(bytes, 0, bytes.Length);
}
}
return "*";
}
else return charChinese;
}
//空方法
private void EmptyFun()
{
}
~AutoComplete()
{
rec_List.Dispose(); //销毁List控件
}
}
}
</span>