当用户在文本框中键入时,提供即时搜索功能的最佳方式是什么

时间:2013-04-25 12:55:04

标签: c# winforms textbox

我希望用户按名称或员工编号搜索员工。我提供了一个文本框。因此,当用户在文本框中键入时,我处理_TextChanged事件并使用员工列表更新dataGridview,其员工姓名或员工编号包含用户在文本框中输入的文本。

我遇到的问题是这会减慢输入和datagridview更新,因为每次文本框中的文本更改时,我的搜索查询都会命中数据库。这使得表单有些无法响应。

有人知道更好的方法吗?

2 个答案:

答案 0 :(得分:2)

  • 简单:延迟查询数据库半秒或秒,以确保用户通过记住上次更改某些文本来停止输入。
  • 长期更好:如果db查询需要很长时间(超过一秒),那么您可以将数据库外包查询到 另一个线程(任务或后台工作者)。你甚至可以填补 DataGrid在一个自己的Task中,如果它的数据太多,那么绘图需要很长时间。你也可以 实现一些取消机制,你有你的主GUI 元素保持敏感。

我想到了下面的民主代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication19
{
    public partial class Form1 : Form
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        public Form1()
        {
            InitializeComponent();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            // cancel old query and datagrid update
            tokenSource.Cancel();

            tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;

            Task.Factory.StartNew((s) =>
                {
                    var q = Task.Factory.StartNew<IEnumerable<DemoData>>(() => LongLastingDataQuery(textBox1.Text, token), token);
                    if (!token.IsCancellationRequested)
                        Task.Factory.StartNew(() => BindData(q.Result));
                }, token);
        }

        private IEnumerable<DemoData> LongLastingDataQuery(string search, CancellationToken token)
        {
            List<DemoData> l = new List<DemoData>();
            for (int i = 0; i < 10000 * search.Length; i++)
            {
                if (token.IsCancellationRequested)
                    return l;

                l.Add(new DemoData { ID = i, Text = search + i, Text1 = search + i + i, Text2 = search + i + i + i, Text3 = search + i + i + i + i });
            }
            Thread.Sleep(1000);
            return l;
        }

        private void BindData(IEnumerable<DemoData> enumerable)
        {
            if (dataGridView1.InvokeRequired)
                dataGridView1.Invoke(new MethodInvoker(() => BindData(enumerable)));
            else
            {
                demoDataBindingSource.DataSource = null;
                demoDataBindingSource.DataSource = enumerable;
            }
        }

        public class DemoData
        {
            public string Text { get; set; }
            public string Text1 { get; set; }
            public string Text2 { get; set; }
            public string Text3 { get; set; }
            public int ID { get; set; }
        }
    }
}

答案 1 :(得分:0)

我有两条建议:

  1. 处理“TextBox enter KeyPressdown”事件而不是TextChange事件,因此只有在键入查询后按下“Return”键时才会触发事件。示例代码如下:

    private void searchTextBox_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)Keys.Return)
        {
            // DoQueryAndRebindEmployees(searchTextBox.Text);
        }
    }
    
  2. 如果您坚持在TextChange事件中处理,请将数据库搜索代码放在另一个线程中,例如在BackgroundWorker线程中, 在TextChange事件处理程序中:

     BackgroundWorker employeeQueryWorker = new BackgroundWorker();
        employeeQueryWorker.DoWork += new DoWorkEventHandler(employeeQueryWorker_DoWork);
        employeeQueryWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(employeeQueryWorker_RunWorkerCompleted);
        employeeQueryWorker.RunWorkerAsync(searchTextBox.Text);
    
  3. 要在Backgroundwork线程和UI线程之间进行通信,您可以定义类的成员变量来表示员工查询结果,例如

     private IList<Employee>  m_employeeQueryResult = null;
    

    然后在employeeQueryWorker_DoWork(对象发送者,DoWorkEventArgs e)线程方法中,执行耗时的数据库查询并将查询结果存储到m_employeeQueryResult中。

    最后,在employeeQueryWorker_RunWorkerCompleted()方法中,将m_employeeQueryResult与DataGridView控件绑定。

    这将使UI控件保持响应。