public class AsSearchTextBox : TextBox {
#region 字段
/// <summary>
/// 列表框
/// </summary>
private ListBox listBox;
/// <summary>
/// 记住前输入的字符串
/// </summary>
private string oldText;
/// <summary>
/// 显示面板
/// </summary>
private Panel panel;
private object _lockObj = new object();
#endregion Fields
#region 属性
public List<string> AutoCompleteList {
get;
set;
}
/// <summary>
/// 在显示之前键入的最小字符
/// </summary>
public int MinTypedCharacters {
get;
set;
}
/// <summary>
/// 选择索引值
/// </summary>
public int SelectedIndex {
get {
return listBox.SelectedIndex;
}
set {
// 不能为空
if (listBox.Items.Count != 0) listBox.SelectedIndex = value;
}
}
/// <summary>
/// 当前显示的实际列表
/// </summary>
[Description("总数据源"), DefaultValue(null)]
public List<string> CurrentAutoCompleteList {
set;
get;
}
/// <summary>
/// 该控件的父窗体
/// </summary>
private Form ParentForm {
get { return this.Parent.FindForm(); }
}
#endregion
#region 构造函数
public AsSearchTextBox() : base() {
// 分配一些默认值
// 在显示之前键入的最小字符
this.MinTypedCharacters = 1;
// 我们建议数据库的列表
this.AutoCompleteList = new List<string>();
// 列表框
this.listBox = new ListBox();
this.listBox.Name = " SuggestionListBox";
this.listBox.Font = this.Font;
this.listBox.Visible = true;
// 这个容器用来保持列表框所在位置
this.panel = new Panel();
this.panel.Visible = false;
this.panel.Font = this.Font;
// 能够适应父窗体的大小更改
this.panel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
// 初始化最小尺寸以避免重叠或闪烁问题
this.panel.ClientSize = new Size(1, 1);
this.panel.Name = " SuggestionPanel";
this.panel.Padding = new Padding(0, 0, 0, 0);
this.panel.Margin = new Padding(0, 0, 0, 0);
this.panel.BackColor = Color.Transparent;
this.panel.ForeColor = Color.Transparent;
this.panel.Text = " ";
this.panel.PerformLayout();
// 控件是否存在容器里
if (!panel.Controls.Contains(listBox))
this.panel.Controls.Add(listBox);
// 让ListBox填充容器
this.listBox.Dock = DockStyle.Fill;
// 只有一项可以选择
this.listBox.SelectionMode = SelectionMode.One;
// 事件
this.listBox.KeyDown += new KeyEventHandler(listBox_KeyDown);
this.listBox.MouseClick += new MouseEventHandler(listBox_MouseClick);
this.listBox.MouseDoubleClick += new MouseEventHandler(listBox_MouseDoubleClick);
#region 备注: ArrayList与List<string>
// 令人惊奇的是ArrayList比List<string>快一点
// 使用ArrayList而替换List<string>”
// 使用List<string>泛型类
#endregion 备注: ArrayList与List<string>
this.CurrentAutoCompleteList = new List<string>();
#region 备注: DataSource 与 AddRange
// 使用数据源比添加项目更快(见注释listbox.items.addrange方法如下)
#endregion 备注: DataSource 与 AddRange
// currentautocompletelist作为数据源列表
listBox.DataSource = CurrentAutoCompleteList;
// 设置输入历史
oldText = this.Text;
}
#endregion
#region 隐藏ListBox
/// <summary>
/// 隐藏ListBox
/// </summary>
public void HideSuggestionListBox() {
if ((ParentForm != null)) {
// 隐藏容器也隐藏列表
panel.Hide();
// 是否存在窗体中
if (this.ParentForm.Controls.Contains(panel))
this.ParentForm.Controls.Remove(panel);
}
}
#endregion
#region 重载键盘响应事件
/// <summary>
/// 重载键盘响应事件
/// </summary>
/// <param name="e"></param>
protected override void OnKeyDown(KeyEventArgs args) {
// 如果用户按key.up
if ((args.KeyCode == Keys.Up)) {
MoveSelectionInListBox((SelectedIndex - 1));
// 完成工作
args.Handled = true;
} else if ((args.KeyCode == Keys.Down)) {
MoveSelectionInListBox((SelectedIndex + 1));
args.Handled = true;
} else if ((args.KeyCode == Keys.PageUp)) {
MoveSelectionInListBox((SelectedIndex - 10));
args.Handled = true;
} else if ((args.KeyCode == Keys.PageDown)) {
MoveSelectionInListBox((SelectedIndex + 10));
args.Handled = true;
} else if ((args.KeyCode == Keys.Enter)) {
SelectItem();
args.Handled = true;
} else
base.OnKeyDown(args);
}
#endregion
#region 重载失去焦点事件
/// <summary>
/// 重载失去焦点事件
/// </summary>
/// <param name="e"></param>
protected override void OnLostFocus(System.EventArgs e) {
if (!panel.ContainsFocus) {
// 调用基类的事件
base.OnLostFocus(e);
// 隐藏
this.HideSuggestionListBox();
}
}
#endregion
#region 重载文本框文本改变事件
/// <summary>
/// 重载文本框文本改变事件
/// </summary>
/// <param name="e"></param>
protected override void OnTextChanged(EventArgs args) {
// 避免崩溃
if (!this.DesignMode)
ShowSuggests();
base.OnTextChanged(args);
// 记录输入
oldText = this.Text;
}
#endregion
#region ListBox按键事件
/// <summary>
/// ListBox按键事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) {
if (e.KeyCode == Keys.Enter) {
//选择当前项目
SelectItem();
e.Handled = true;
}
}
#endregion
#region ListBox鼠标单击事件
/// <summary>
/// ListBox鼠标单击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) {
SelectItem();
}
#endregion
#region ListBox鼠标双击事件
/// <summary>
/// ListBox鼠标双击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listBox_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) {
SelectItem();
}
#endregion
#region 移动ListBox项选择索引
/// <summary>
/// 移动ListBox项选择索引
/// </summary>
/// <param name="index"></param>
private void MoveSelectionInListBox(int Index) {
if (Index <= -1) this.SelectedIndex = 0;
else {
if (Index > (listBox.Items.Count - 1))
SelectedIndex = (listBox.Items.Count - 1);
else SelectedIndex = Index;
}
}
#endregion
#region 选择项
/// <summary>
/// 选择项
/// </summary>
/// <returns></returns>
private bool SelectItem() {
// 如果列表不为空
if (((this.listBox.Items.Count > 0) && (this.SelectedIndex > -1))) {
//Model m = this.listBox.SelectedItem as Model;
// 设置选定的文本
//this.Text = m.Text;
//this.Tag = m.Id;
this.Text = this.listBox.SelectedItem.ToString();
// 隐藏
this.HideSuggestionListBox();
}
return true;
}
#endregion
#region 显示敏感词汇
/// <summary>
/// 显示敏感词汇
/// </summary>
private void ShowSuggests() {
// 如果输入框文字长度大于限制长度
if (this.Text.Length >= MinTypedCharacters) {
// 防止与其他控件重叠的问题
// 在加载数据时没有绘制,所以挂起布局
panel.SuspendLayout();
// 用户正在键入前,在输入框已经添加了字符
if ((this.Text.Length > 0) && (this.oldText == this.Text.Substring(0, this.Text.Length - 1)))
//处理选择项与刷新
UpdateCurrentAutoCompleteList();
// 用户键入的字符被删除后
else if ((this.oldText.Length > 0) && (this.Text == this.oldText.Substring(0, this.oldText.Length - 1)))
UpdateCurrentAutoCompleteList();
// 文本框的发生改变
else
UpdateCurrentAutoCompleteList();
if (((CurrentAutoCompleteList != null) && CurrentAutoCompleteList.Count > 0)) {
// 最后显示面板和列表框
// 刷新以防止绘制空矩形
panel.Show();
// 设置在所有控件的顶部
panel.BringToFront();
// 然后把焦点回到文本框
this.Focus();
} else
this.HideSuggestionListBox();
// 防止与其他控件重叠的问题
panel.ResumeLayout(true);
} else
this.HideSuggestionListBox();
}
#endregion
#region 修改当前数据源
/// <summary>
/// 修改当前数据源
/// </summary>
private void UpdateCurrentAutoCompleteList() {
// 清除列表项
CurrentAutoCompleteList.Clear();
foreach (string Str in AutoCompleteList) {
// 为查找子串(相当于SQL命令)
if ((Str.IndexOf(this.Text) > -1))
CurrentAutoCompleteList.Add(Str);
else if ((Str.ToLower().IndexOf(this.Text.ToLower()) > -1))
CurrentAutoCompleteList.Add(Str);
}
#region 备注: LINQ查询的性能测量
// 例子,LINQ测量
/*
CurrentAutoCompleteList.Clear();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
// using Linq query seems to be slower (twice as slow)
var query =
from expression in this.AutoCompleteList
where expression.ToLower().Contains(this.Text.ToLower())
select expression;
foreach (string searchTerm in query)
{
CurrentAutoCompleteList.Add(searchTerm);
}
stopWatch.Stop();
// 用于将性能值打印到控制台的方法(见下)
PrintStopwatch(stopWatch, "Linq - Contains");
*/
#endregion 备注: LINQ查询的性能测量
// 继续更新列表框的UI部分
UpdateListBoxItems();
}
#endregion
#region 修改ListBox数据源
/// <summary>
/// 修改ListBox数据源
/// </summary>
private void UpdateListBoxItems() {
lock (_lockObj) {
if (CurrentAutoCompleteList.Count <= 0) return;
// 如果父窗体不为空
if ((ParentForm != null)) {
//获取宽度
panel.Width = this.Width;
if (CurrentAutoCompleteList.Count >= 10)
// 容器高度
panel.Height = this.listBox.ItemHeight * 10;
else if (CurrentAutoCompleteList.Count <= 3)
panel.Height = this.listBox.ItemHeight * CurrentAutoCompleteList.Count + this.listBox.ItemHeight;
else
panel.Height = this.listBox.ItemHeight * CurrentAutoCompleteList.Count;
//容器显示位置
panel.Location = new Point(this.Parent.Location.X + this.Location.X, this.Parent.Location.Y + this.Location.Y + this.Height);
if (!this.ParentForm.Controls.Contains(panel))
this.ParentForm.Controls.Add(panel);
// 更新列表,不支持更改事件
// 这是设置数据源的途径,一个棘手的问题,可能会导致冲突,
// 所以如果回到AddRange方法(见备注)
//((CurrencyManager)listBox.BindingContext[CurrentAutoCompleteList]).Refresh();
this.listBox.DataSource = CurrentAutoCompleteList;
//this.listBox.DisplayMember = "Text";
//this.listBox.ValueMember = "Id";
#region 备注: DataSource 与 AddRange
/*
// 这部分是由于不使用“数据源”列表框。方法(参见构造函数)
// 留作比较,生产中删除使用
listBox.BeginUpdate();
listBox.Items.Clear();
//Fills the ListBox
listBox.Items.AddRange(this.CurrentAutoCompleteList.ToArray());
listBox.EndUpdate();
// 要使用此方法删除以下
// "((CurrencyManager)listBox.BindingContext[CurrentAutoCompleteList]).Refresh();" line and
// "listBox.DataSource = CurrentAutoCompleteList;" line from the constructor
*/
#endregion 备注: DataSource 与 AddRange
}
}
}
#endregion
#region 需要进行性能测试时使用-生产环境删除使用
/// <summary>
/// 需要进行性能测试时使用-生产环境删除使用
/// </summary>
/// <param name="stopWatch"></param>
/// <param name="comment"></param>
/*private void PrintStopwatch(Stopwatch stopWatch, string comment)
{
// 把时间作为一个TimeSpan值。
TimeSpan ts = stopWatch.Elapsed;
// Format and display the TimeSpan value.
string elapsedTime = String.Format("{0:00}h:{1:00}m:{2:00},{3:000}s \t {4}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds, comment);
Console.WriteLine("RunTime " + elapsedTime);
}*/
#endregion
//public class Model {
// /// <summary>
// /// 显示文本
// /// </summary>
// public string Text { get; set; }
// /// <summary>
// /// 对应唯一值
// /// </summary>
// public string Id { get; set; }
// /// <summary>
// /// 对象
// /// </summary>
// public object Tag { get; set; }
//}
}
文章评论