Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当用户在文本框中键入时,提供即时搜索功能的最佳方式是什么_C#_Winforms_Textbox - Fatal编程技术网

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

C# 当用户在文本框中键入时,提供即时搜索功能的最佳方式是什么,c#,winforms,textbox,C#,Winforms,Textbox,我希望用户按姓名或员工编号搜索员工。我提供了一个文本框。因此,当用户在文本框中键入时,我将处理_TextChanged事件,并使用员工列表更新dataGridview,其中员工姓名或员工编号包含用户在文本框中输入的文本 我遇到的问题是,这会减慢键入和datagridview更新的速度,因为每次文本框中的文本更改时,我的搜索查询都会命中数据库。这使得表单有些不响应 有人知道更好的方法吗? 简单:延迟查询数据库半秒或几秒钟,以确保用户通过记住上一次文本更改来停止键入 从长远来看更好:如果数据库查询需

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

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

有人知道更好的方法吗?

  • 简单:延迟查询数据库半秒或几秒钟,以确保用户通过记住上一次文本更改来停止键入
  • 从长远来看更好:如果数据库查询需要很长时间(超过一秒钟),那么您可以将查询数据库的工作外包给 另一个线程(任务或backgroundworker)。你甚至可以填补这个空缺 如果数据量太大以至于绘图需要很长时间,那么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; }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows.Forms;
命名空间Windows窗体应用程序19
{
公共部分类Form1:Form
{
CancellationTokenSource tokenSource=新的CancellationTokenSource();
公共表格1()
{
初始化组件();
}
私有void textBox1\u TextChanged(对象发送方,事件参数e)
{
//取消旧查询和数据网格更新
tokenSource.Cancel();
tokenSource=新的CancellationTokenSource();
var token=tokenSource.token;
Task.Factory.StartNew((s)=>
{
var q=Task.Factory.StartNew(()=>LongLastingDataQuery(textBox1.Text,token),token);
如果(!token.IsCancellationRequested)
Task.Factory.StartNew(()=>BindData(q.Result));
},代币);
}
私有IEnumerable LongLastingDataQuery(字符串搜索、取消令牌)
{
列表l=新列表();
for(int i=0;i<10000*search.Length;i++)
{
if(令牌.IsCancellationRequested)
返回l;
l、 添加(新解调数据{ID=i,Text=search+i,Text1=search+i+i,Text2=search+i+i,Text3=search+i+i+i});
}
睡眠(1000);
返回l;
}
私有void BindData(IEnumerable可枚举)
{
if(dataGridView1.InvokeRequired)
调用(新的MethodInvoker(()=>BindData(可枚举));
其他的
{
demoDataBindingSource.DataSource=null;
demoDataBindingSource.DataSource=可枚举;
}
}
公共类蠕形螨
{
公共字符串文本{get;set;}
公共字符串Text1{get;set;}
公共字符串Text2{get;set;}
公共字符串Text3{get;set;}
公共int ID{get;set;}
}
}
}

我有两个建议:

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

    private void searchTextBox_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)Keys.Return)
        {
            // DoQueryAndRebindEmployees(searchTextBox.Text);
        }
    }
    
  • 如果坚持在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线程之间通信,可以定义类的成员变量来表示employee查询结果,如

     private IList<Employee>  m_employeeQueryResult = null;
    
    private IList m_employeeQueryResult=null;
    
    然后在employeeQueryWorker_DoWork(对象发送方,DoWorkEventTargets e)线程方法中,执行耗时的数据库查询,并将查询结果存储到m_employeeQueryResult中

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


    这将使UI控件保持响应。

    您应该尝试在列表中缓存数据库结果。然后从列表而不是
    \u TextChanged
    上的数据库中提取我支持的此评论您可以让它仅在文本框中有一定数量的字符时搜索数据库,例如3。然后按照gwin003的建议,将这些结果粘贴到列表中,对于大于3的字符,从该列表中绘制结果。这样,如果字符退格到<3,它将只运行另一个查询。添加了一些工作演示代码来演示如何使用嵌套任务完成此操作。