0

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

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

有人知道更好的方法吗?

4

2 回答 2

2
  • 很简单:延迟查询数据库半秒或秒,以确保用户通过记住上次更改某些文本的时间来停止输入。
  • 从长远来看更好:如果数据库查询需要很长时间(超过一秒),那么您可以将查询数据库外包给另一个线程(任务或后台工作人员)。如果数据太多以至于绘制需要很长时间,您甚至可以在自己的任务中填充 DataGrid。此外,您将能够实现一些取消机制,并且您的主要 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; }
        }
    }
}
于 2013-04-25T13:09:44.377 回答
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);
    

Backgroundwork线程和UI线程之间的通信,可以定义一个类的成员变量来表示员工查询结果,比如

 private IList<Employee>  m_employeeQueryResult = null;

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

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

这将使 UI 控件保持响应。

于 2013-04-25T14:01:00.113 回答