WinForm 自动完成控件实例代码简析
程序员文章站
2023-12-12 19:51:40
在web的应用方面有js的插件实现自动完成(或叫智能提示)功能,但在winform窗体应用方面就没那么好了。textbox控件本身是提供了一个自动提示功能,只要用上这三个属...
在web的应用方面有js的插件实现自动完成(或叫智能提示)功能,但在winform窗体应用方面就没那么好了。
textbox控件本身是提供了一个自动提示功能,只要用上这三个属性:
autocompletecustomsource:autocompletesource 属性设置为customsource 时要使用的 stringcollection。
autocompletemode:指示文本框的文本完成行为。
autocompletesource:自动完成源,可以是 autocompletesource 的枚举值之一。
就行了, 一个简单的示例如下
textbox1.autocompletecustomsource .addrange(new string[] { "java","javascript","js","c#","c","c++" });
textbox1.autocompletemode = autocompletemode.suggestappend;
textbox1.autocompletesource = autocompletesource.customsource;
可是这种方式的不支持我们中文的简拼自动完成(如在文本框里输入"gz"就会出现"广州")。只好自己写一个支持简拼自动完成的控件了。
这是效果图
控件不太复杂,一个textbox和一个listbox。代码方面,用datatable作数据源,每次在textbox的值时,通过datatable的select方法,配上合适的表达式(如:{0} like '{1}%' and isnull([{2}], ' ') <> ' ')来筛选出合适的备选文本内容,以下则是控件的代码:
private textbox _tb;
private listbox _lb;
private datatable _dt_datasource;
private bool _text_lock;
private string _general_text;//原始输入文本框的值
private bool _lb_kd_first_top;//listbox是否第一次到达顶部
private int _itemcount;
/// <summary>
/// textbox的text属性,增加了_text_lock操作,放置触发textchanged事件
/// </summary>
private string textboxtext
{
get { return _tb.text; }
set
{
_text_lock = true;
_tb.text = value;
_text_lock = false;
}
}
/// <summary>
/// 显示在listbox的字段名
/// </summary>
public string valuename { get; set; }
/// <summary>
/// 用于匹配的字段名
/// </summary>
public string codename { get; set; }
/// <summary>
/// 显示提示项的数量
/// </summary>
public int itemcount
{
get
{ return _itemcount; }
set
{
if (value <= 0)
_itemcount = 1;
else
_itemcount = value;
}
}
public datatable datasource
{
get { return _dt_datasource; }
set { _dt_datasource = value; }
}
public autocomplete()
{
initialcontrols();
}
void autocomplete_load(object sender, eventargs e)
{
_tb.width = this.width;
_lb.width = _tb.width;
this.height = _tb.height-1;
}
void autocomplete_lostfocus(object sender, eventargs e)
{
_lb.visible = false;
this.height = _tb.height-1;
}
//列表框按键事件
void _lb_keydown(object sender, keyeventargs e)
{
if (_lb.items.count == 0 || !_lb.visible) return;
if (!_lb_kd_first_top && ((e.keycode == keys.up && _lb.selectedindex == 0) || (e.keycode == keys.down && _lb.selectedindex == _lb.items.count)))
{
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
textboxtext = ((datarowview)_lb.selecteditem)[valuename].tostring();
_lb_kd_first_top = _lb.selectedindex != 0;
}
if (e.keycode == keys.enter && _lb.selectedindex != -1)
{
_lb.visible = false;
this.height = _tb.height;
_tb.focus();
}
}
//列表鼠标单击事件
void _lb_click(object sender, eventargs e)
{
if (_lb.selectedindex != -1)
{
textboxtext = ((datarowview)_lb.selecteditem)[valuename].tostring();
}
_lb.visible = false;
_tb.focus();
this.height = _tb.height;
}
//文本框按键事件
void _tb_keydown(object sender, keyeventargs e)
{
if (_lb.items.count == 0||!_lb.visible) return;
bool _is_set = false;
if (e.keycode == keys.up)
{
if (_lb.selectedindex <= 0)
{
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
_lb.selectedindex--;
_is_set = true;
}
}
else if (e.keycode == keys.down)
{
if (_lb.selectedindex == _lb.items.count - 1)
{
_lb.selectedindex = 0;
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
_lb.selectedindex++;
_is_set = true;
}
}
else if (e.keycode == keys.enter)
{
_lb.visible = false;
this.height = _tb.height;
_is_set = _lb.selectedindex != -1;
}
_lb_kd_first_top = _lb.selectedindex != 0;
if (_is_set)
{
_text_lock = true;
_tb.text = ((datarowview)_lb.selecteditem)[valuename].tostring();
_tb.selectionstart = _tb.text.length + 10;
_tb.selectionlength = 0;
_text_lock = false;
}
}
//文本框文本变更事件
void _tb_textchanged(object sender, eventargs e)
{
if (_text_lock) return;
_general_text = _tb.text;
_lb.visible = true;
_lb.height = _lb.itemheight * (_itemcount+1);
this.bringtofront();
_lb.bringtofront();
this.height = _tb.height + _lb.height;
datatable temp_table = _dt_datasource.clone();
string filtstr = formatstr(_tb.text);
datarow [] rows = _dt_datasource.select(string.format(getfilterstr(),codename,filtstr,_lb.displaymember));
for (int i = 0; i < rows.length&&i<_itemcount; i++)
{
temp_table.rows.add(rows[i].itemarray);
}
_lb.datasource = temp_table;
if (_lb.items.count > 0) _lb.selecteditem = _lb.items[0];
}
/// <summary>
/// 初始化控件
/// </summary>
private void initialcontrols()
{
_lb_kd_first_top = true;
_tb = new textbox();
_tb.location = new point(0, 0);
_tb.margin = new system.windows.forms.padding(0);
_tb.width = this.width;
_tb.textchanged += new eventhandler(_tb_textchanged);
_tb.keyup += new keyeventhandler(_tb_keydown);
_lb = new listbox();
_lb.visible = false;
_lb.width = _tb.width;
_lb.margin = new system.windows.forms.padding(0);
_lb.displaymember = valuename;
_lb.selectionmode = selectionmode.one;
_lb.location = new point(0, _tb.height);
_lb.keyup += new keyeventhandler(_lb_keydown);
_lb.click += new eventhandler(_lb_click);
this.controls.add(_tb);
this.controls.add(_lb);
this.height = _tb.height - 1;
this.lostfocus += new eventhandler(autocomplete_lostfocus);
this.leave += new eventhandler(autocomplete_lostfocus);
this.load += new eventhandler(autocomplete_load);
}
/// <summary>
/// 获取过滤格式字符串
/// </summary>
/// <returns></returns>
private string getfilterstr()
{
//未过滤注入的字符 ' ] %任意 *任意
string filter = " {0} like '{1}%' and isnull([{2}], ' ') <> ' ' ";
if (_dt_datasource.rows[0][codename].tostring().lastindexof(';') > -1)
filter = " {0} like '%;{1}%' and isnull([{2}],' ') <> ' ' ";
return filter;
}
/// <summary>
/// 过滤字符串中一些可能造成出错的字符
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string formatstr(string str)
{
if (string.isnullorempty(str)) return string.empty;
str = str.replace("[", "[[]").replace("%", "[%]").replace("*", "[*]").replace("'", "''");
if (codename == "code") str = str.replace(" ", "");
return str;
}
下面是使用控件的例子
class common
{
/// <summary>
/// 生成测试数据源
/// </summary>
public static datatable createtestdatasoucre
{
get
{
list<keyvaluepair<string, string>> source = new list<keyvaluepair<string, string>>()
{
new keyvaluepair<string,string>("张三",";zs;张三;"),
new keyvaluepair<string,string>("李四",";li;李四;"),
new keyvaluepair<string,string>("王五",";ww;王五;"),
new keyvaluepair<string,string>("赵六",";zl;赵六;"),
new keyvaluepair<string,string>("洗刷",";cs;csharp;c#;洗刷;"),
new keyvaluepair<string,string>("爪哇",";java;爪哇;"),
new keyvaluepair<string,string>("java",";java;"),
new keyvaluepair<string,string>("c#",";c#;cs;csharp;"),
new keyvaluepair<string,string>("javascript",";javascript;js;")
};
datatable table = new datatable();
table.columns.add("id");
table.columns.add("name");
table.columns.add("code");
for (int i = 0; i < source.count; i++)
{
datarow row = table.rows.add();
row["id"] = i;
row["name"] = source[i].key;
row["code"] = source[i].value;
}
return table;
}
}
}
//.............
autocomplete ac=new autocomplete();
ac.valuename = "name";
ac.codename = "code";
ac.datasource= common.createtestdatasoucre;
ac.itemcount= 5;
textbox控件本身是提供了一个自动提示功能,只要用上这三个属性:
autocompletecustomsource:autocompletesource 属性设置为customsource 时要使用的 stringcollection。
autocompletemode:指示文本框的文本完成行为。
autocompletesource:自动完成源,可以是 autocompletesource 的枚举值之一。
就行了, 一个简单的示例如下
复制代码 代码如下:
textbox1.autocompletecustomsource .addrange(new string[] { "java","javascript","js","c#","c","c++" });
textbox1.autocompletemode = autocompletemode.suggestappend;
textbox1.autocompletesource = autocompletesource.customsource;
可是这种方式的不支持我们中文的简拼自动完成(如在文本框里输入"gz"就会出现"广州")。只好自己写一个支持简拼自动完成的控件了。
这是效果图
控件不太复杂,一个textbox和一个listbox。代码方面,用datatable作数据源,每次在textbox的值时,通过datatable的select方法,配上合适的表达式(如:{0} like '{1}%' and isnull([{2}], ' ') <> ' ')来筛选出合适的备选文本内容,以下则是控件的代码:
复制代码 代码如下:
private textbox _tb;
private listbox _lb;
private datatable _dt_datasource;
private bool _text_lock;
private string _general_text;//原始输入文本框的值
private bool _lb_kd_first_top;//listbox是否第一次到达顶部
private int _itemcount;
复制代码 代码如下:
/// <summary>
/// textbox的text属性,增加了_text_lock操作,放置触发textchanged事件
/// </summary>
private string textboxtext
{
get { return _tb.text; }
set
{
_text_lock = true;
_tb.text = value;
_text_lock = false;
}
}
/// <summary>
/// 显示在listbox的字段名
/// </summary>
public string valuename { get; set; }
/// <summary>
/// 用于匹配的字段名
/// </summary>
public string codename { get; set; }
/// <summary>
/// 显示提示项的数量
/// </summary>
public int itemcount
{
get
{ return _itemcount; }
set
{
if (value <= 0)
_itemcount = 1;
else
_itemcount = value;
}
}
public datatable datasource
{
get { return _dt_datasource; }
set { _dt_datasource = value; }
}
复制代码 代码如下:
public autocomplete()
{
initialcontrols();
}
复制代码 代码如下:
void autocomplete_load(object sender, eventargs e)
{
_tb.width = this.width;
_lb.width = _tb.width;
this.height = _tb.height-1;
}
void autocomplete_lostfocus(object sender, eventargs e)
{
_lb.visible = false;
this.height = _tb.height-1;
}
复制代码 代码如下:
//列表框按键事件
void _lb_keydown(object sender, keyeventargs e)
{
if (_lb.items.count == 0 || !_lb.visible) return;
if (!_lb_kd_first_top && ((e.keycode == keys.up && _lb.selectedindex == 0) || (e.keycode == keys.down && _lb.selectedindex == _lb.items.count)))
{
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
textboxtext = ((datarowview)_lb.selecteditem)[valuename].tostring();
_lb_kd_first_top = _lb.selectedindex != 0;
}
if (e.keycode == keys.enter && _lb.selectedindex != -1)
{
_lb.visible = false;
this.height = _tb.height;
_tb.focus();
}
}
//列表鼠标单击事件
void _lb_click(object sender, eventargs e)
{
if (_lb.selectedindex != -1)
{
textboxtext = ((datarowview)_lb.selecteditem)[valuename].tostring();
}
_lb.visible = false;
_tb.focus();
this.height = _tb.height;
}
复制代码 代码如下:
//文本框按键事件
void _tb_keydown(object sender, keyeventargs e)
{
if (_lb.items.count == 0||!_lb.visible) return;
bool _is_set = false;
if (e.keycode == keys.up)
{
if (_lb.selectedindex <= 0)
{
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
_lb.selectedindex--;
_is_set = true;
}
}
else if (e.keycode == keys.down)
{
if (_lb.selectedindex == _lb.items.count - 1)
{
_lb.selectedindex = 0;
_lb.selectedindex = -1;
textboxtext = _general_text;
}
else
{
_lb.selectedindex++;
_is_set = true;
}
}
else if (e.keycode == keys.enter)
{
_lb.visible = false;
this.height = _tb.height;
_is_set = _lb.selectedindex != -1;
}
_lb_kd_first_top = _lb.selectedindex != 0;
if (_is_set)
{
_text_lock = true;
_tb.text = ((datarowview)_lb.selecteditem)[valuename].tostring();
_tb.selectionstart = _tb.text.length + 10;
_tb.selectionlength = 0;
_text_lock = false;
}
}
//文本框文本变更事件
void _tb_textchanged(object sender, eventargs e)
{
if (_text_lock) return;
_general_text = _tb.text;
_lb.visible = true;
_lb.height = _lb.itemheight * (_itemcount+1);
this.bringtofront();
_lb.bringtofront();
this.height = _tb.height + _lb.height;
datatable temp_table = _dt_datasource.clone();
string filtstr = formatstr(_tb.text);
datarow [] rows = _dt_datasource.select(string.format(getfilterstr(),codename,filtstr,_lb.displaymember));
for (int i = 0; i < rows.length&&i<_itemcount; i++)
{
temp_table.rows.add(rows[i].itemarray);
}
_lb.datasource = temp_table;
if (_lb.items.count > 0) _lb.selecteditem = _lb.items[0];
}
复制代码 代码如下:
/// <summary>
/// 初始化控件
/// </summary>
private void initialcontrols()
{
_lb_kd_first_top = true;
_tb = new textbox();
_tb.location = new point(0, 0);
_tb.margin = new system.windows.forms.padding(0);
_tb.width = this.width;
_tb.textchanged += new eventhandler(_tb_textchanged);
_tb.keyup += new keyeventhandler(_tb_keydown);
_lb = new listbox();
_lb.visible = false;
_lb.width = _tb.width;
_lb.margin = new system.windows.forms.padding(0);
_lb.displaymember = valuename;
_lb.selectionmode = selectionmode.one;
_lb.location = new point(0, _tb.height);
_lb.keyup += new keyeventhandler(_lb_keydown);
_lb.click += new eventhandler(_lb_click);
this.controls.add(_tb);
this.controls.add(_lb);
this.height = _tb.height - 1;
this.lostfocus += new eventhandler(autocomplete_lostfocus);
this.leave += new eventhandler(autocomplete_lostfocus);
this.load += new eventhandler(autocomplete_load);
}
/// <summary>
/// 获取过滤格式字符串
/// </summary>
/// <returns></returns>
private string getfilterstr()
{
//未过滤注入的字符 ' ] %任意 *任意
string filter = " {0} like '{1}%' and isnull([{2}], ' ') <> ' ' ";
if (_dt_datasource.rows[0][codename].tostring().lastindexof(';') > -1)
filter = " {0} like '%;{1}%' and isnull([{2}],' ') <> ' ' ";
return filter;
}
/// <summary>
/// 过滤字符串中一些可能造成出错的字符
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string formatstr(string str)
{
if (string.isnullorempty(str)) return string.empty;
str = str.replace("[", "[[]").replace("%", "[%]").replace("*", "[*]").replace("'", "''");
if (codename == "code") str = str.replace(" ", "");
return str;
}
下面是使用控件的例子
复制代码 代码如下:
class common
{
/// <summary>
/// 生成测试数据源
/// </summary>
public static datatable createtestdatasoucre
{
get
{
list<keyvaluepair<string, string>> source = new list<keyvaluepair<string, string>>()
{
new keyvaluepair<string,string>("张三",";zs;张三;"),
new keyvaluepair<string,string>("李四",";li;李四;"),
new keyvaluepair<string,string>("王五",";ww;王五;"),
new keyvaluepair<string,string>("赵六",";zl;赵六;"),
new keyvaluepair<string,string>("洗刷",";cs;csharp;c#;洗刷;"),
new keyvaluepair<string,string>("爪哇",";java;爪哇;"),
new keyvaluepair<string,string>("java",";java;"),
new keyvaluepair<string,string>("c#",";c#;cs;csharp;"),
new keyvaluepair<string,string>("javascript",";javascript;js;")
};
datatable table = new datatable();
table.columns.add("id");
table.columns.add("name");
table.columns.add("code");
for (int i = 0; i < source.count; i++)
{
datarow row = table.rows.add();
row["id"] = i;
row["name"] = source[i].key;
row["code"] = source[i].value;
}
return table;
}
}
}
//.............
autocomplete ac=new autocomplete();
ac.valuename = "name";
ac.codename = "code";
ac.datasource= common.createtestdatasoucre;
ac.itemcount= 5;