WinForm 自动完成控件实例代码简析

在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;

(0)

相关推荐

  • WinForm自定义函数FindControl实现按名称查找控件

    本文所述实例实现WinForm自定义函数FindControl实现按名称查找控件的功能,在C#程序开发中有一定的实用价值.分享给大家供大家参考. 关键代码如下: /// <summary> /// 按名称查找控件 /// </summary> /// <param name="parentControl">查找控件的父容器控件</param> /// <param name="findCtrlName">查

  • 深入分析C#中WinForm控件之Dock顺序调整的详解

    最近被.net winform中的控件布局搞困惑了,由于控件都是使用Dock方式的,操作起来也是比较方便,如果最大化,窗口大小调整等,都可以随着窗口大小的变化而变化.但问题是,.net winform的dock方式是根据先添加的控件顺序进行优先级Dock排列的,假设我想目前有三个控件A.B.C分别以 Top.Bottom以及Fill方式填充窗口即窗口看起来应该是这样的:---------------------A---------------------C--------------------

  • 学习Winform文本类控件(Label、Button、TextBox)

    对于一个应用程序而言,控件是搭建用户界面的积木.它们具备交互式的特征.VS 2012中的控件都放在工具箱中,添加一个控件到窗体,只需在工具箱中选择好要添加的控件,并按住鼠标左键,拖动到窗体上即可.向窗体上添加控件还有另一个方法,就是通过代码来实现,即可通过窗体的Controls类的Add方法,格式为:窗体标识.Controls.Add(控件标识).通过代码的方法添加控件比较麻烦,基本上是不会使用这种方法的. 文本类控件包含标签控件(Label),按钮控件(Button),文本框控件(TextBo

  • C#的winform控件命名规范

    本文详细讲述了C#的winform控件命名规范.分享给大家供大家参考.具体如下: 注:这里用红字标记的部分表示有重复出现,括号内为替代表示方案 1.标准控件 序号 控件类型简写 控件类型 1 btn Button 2 chk CheckBox 3 ckl CheckedListBox 4 cmb ComboBox 5 dtp DateTimePicker 6 lbl Label 7 llb LinkLabel 8 lst ListBox 9 lvw ListView 10 mtx MaskedT

  • .Net WInform开发笔记(三)谈谈自制控件(自定义控件)

    末日这天写篇博客吧,既然没来,那就纪念一下. 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能 2.重写ListBox,增加折叠.鼠标背影.分类等功能 -----------------------------分割线--------------------------------------------------------------一.扩展OpenFileD

  • C#中让控件全屏显示的实现代码(WinForm)

    1.使用winapi "SetParent" 接口: 复制代码 代码如下: [DllImport("user32.dll", SetLastError = true)] static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 复制代码 代码如下: control.Dock = DockStyle.None; control.Left = 0; control.Top = 0; c

  • WinForm子窗体访问父窗体控件的实现方法

    Form1主窗体: 复制代码 代码如下: public delegate void SetVisiableHandler();//定义委托类型 private void button1_Click(object sender,EventArgs e)//单击事件,打开子窗体{    Form2 frm = new Form2(new SetVisiableHandler(SetVisiable));    frm.Show();} private void SetVisiable(){    d

  • WinForm实现移除控件某个事件的方法

    本文实例讲述了WinForm实现移除控件某个事件的方法,供大家参考借鉴一下.具体功能代码如下: 主要功能部分代码如下: /// <summary> /// 移除控件某个事件 /// </summary> /// <param name="control">控件</param> /// <param name="eventName">需要移除的控件名称eg:EventClick</param> p

  • c#中多线程访问winform控件的若干问题小结

    我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来解决这个问题,下面我将详细的介绍. 首先来看传统方法: 复制代码 代码如下: public partial class Form1 : Form     {        public Form1()        {            InitializeComponent();        } private void Form1_Load(object sender,

  • 简介Winform中创建用户控件

    步骤: 1.创建一个项目,该项目主要用来设计用户控件. 2.创建一个用户控件窗体,用来设计用户控件. 3.向用户控件窗体中添加一个按钮(button1),给它添加相应的移入.移出事件,实现移入时按钮的背景设置一个图片,移出的时候背景设置另一个图片. 复制代码 代码如下: private void button1_MouseEnter(object sender, EventArgs e)        {            this.button1.Image = Image.FromFil

随机推荐