自动完成TextBox实现类似百度搜索框

public class AsSearchTextBox : TextBox {
 
    #region 字段
    /// 
    /// 列表框
    /// 
    private ListBox listBox;
 
    /// 
    /// 记住前输入的字符串
    /// 
    private string oldText;
 
    /// 
    /// 显示面板
    /// 
    private Panel panel;
    private object _lockObj = new object();
    #endregion Fields
 
    #region 属性
    public List AutoCompleteList {
        get;
        set;
    }
 
    /// 
    /// 在显示之前键入的最小字符
    /// 
    public int MinTypedCharacters {
        get;
        set;
    }
 
    /// 
    /// 选择索引值
    /// 
    public int SelectedIndex {
        get {
            return listBox.SelectedIndex;
        }
        set {
            // 不能为空
            if (listBox.Items.Count != 0) listBox.SelectedIndex = value;
        }
    }
 
    /// 
    /// 当前显示的实际列表
    /// 
    [Description("总数据源"), DefaultValue(null)]
    public List CurrentAutoCompleteList {
        set;
        get;
    }
 
    /// 
    /// 该控件的父窗体
    /// 
    private Form ParentForm {
        get { return this.Parent.FindForm(); }
    }
    #endregion
 
    #region 构造函数
    public AsSearchTextBox() : base() {
        // 分配一些默认值
        // 在显示之前键入的最小字符
        this.MinTypedCharacters = 1;
        // 我们建议数据库的列表
        this.AutoCompleteList = new List();
 
        // 列表框
        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
        // 令人惊奇的是ArrayList比List快一点
        // 使用ArrayList而替换List”
        // 使用List泛型类
        #endregion 备注: ArrayList与List
        this.CurrentAutoCompleteList = new List();
 
        #region 备注: DataSource 与 AddRange
        // 使用数据源比添加项目更快(见注释listbox.items.addrange方法如下)
        #endregion 备注: DataSource 与 AddRange
        // currentautocompletelist作为数据源列表
        listBox.DataSource = CurrentAutoCompleteList;
        // 设置输入历史
        oldText = this.Text;
    }
    #endregion
 
    #region 隐藏ListBox
    /// 
    /// 隐藏ListBox
    /// 
    public void HideSuggestionListBox() {
        if ((ParentForm != null)) {
            // 隐藏容器也隐藏列表
            panel.Hide();
            // 是否存在窗体中
            if (this.ParentForm.Controls.Contains(panel))
                this.ParentForm.Controls.Remove(panel);
        }
    }
    #endregion
 
    #region 重载键盘响应事件
    /// 
    /// 重载键盘响应事件
    /// 
    /// 
    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 重载失去焦点事件
    /// 
    /// 重载失去焦点事件
    /// 
    /// 
    protected override void OnLostFocus(System.EventArgs e) {
        if (!panel.ContainsFocus) {
            // 调用基类的事件
            base.OnLostFocus(e);
            // 隐藏
            this.HideSuggestionListBox();
        }
    }
    #endregion
 
    #region 重载文本框文本改变事件
    /// 
    /// 重载文本框文本改变事件
    /// 
    /// 
    protected override void OnTextChanged(EventArgs args) {
        // 避免崩溃
        if (!this.DesignMode)
            ShowSuggests();
        base.OnTextChanged(args);
        // 记录输入
        oldText = this.Text;
    }
    #endregion
 
    #region ListBox按键事件
    /// 
    /// ListBox按键事件
    /// 
    /// 
    /// 
    private void listBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) {
        if (e.KeyCode == Keys.Enter) {
            //选择当前项目
            SelectItem();
            e.Handled = true;
        }
    }
    #endregion
 
    #region ListBox鼠标单击事件
    /// 
    /// ListBox鼠标单击事件
    /// 
    /// 
    /// 
    private void listBox_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) {
        SelectItem();
    }
    #endregion
 
    #region ListBox鼠标双击事件
    /// 
    /// ListBox鼠标双击事件
    /// 
    /// 
    /// 
    private void listBox_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e) {
        SelectItem();
    }
    #endregion
 
    #region 移动ListBox项选择索引
    /// 
    /// 移动ListBox项选择索引
    /// 
    /// 
    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 选择项
    /// 
    /// 选择项
    /// 
    /// 
    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 显示敏感词汇
    /// 
    /// 显示敏感词汇
    /// 
    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 修改当前数据源
    /// 
    /// 修改当前数据源
    /// 
    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数据源
    /// 
    /// 修改ListBox数据源
    /// 
    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 需要进行性能测试时使用-生产环境删除使用
    /// 
    /// 需要进行性能测试时使用-生产环境删除使用
    /// 
    /// 
    /// 
    /*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 {
    //    /// 
    //    /// 显示文本
    //    /// 
    //    public string Text { get; set; }
    //    /// 
    //    /// 对应唯一值
    //    /// 
    //    public string Id { get; set; }
    //    /// 
    //    /// 对象
    //    /// 
    //    public object Tag { get; set; }
    //}
}

你可能感兴趣的:(WinForm,ASP.NET,.NET)