在不更改数据源的情况下过滤 DataGridView

Posted

技术标签:

【中文标题】在不更改数据源的情况下过滤 DataGridView【英文标题】:Filtering DataGridView without changing datasource 【发布时间】:2011-08-16 03:47:27 【问题描述】:

我正在 C# Visual Studio 2010 中开发用户控件 - 一种用于过滤 datagridview 的“快速查找”文本框。它应该适用于 3 种类型的 datagridview 数据源:DataTable、DataBinding 和 DataSet。 我的问题是从显示在 DataGridView 上的 DataSet 对象过滤 DataTable。

可能有 3 种情况(带有 DataGridView 和 TextBox 的标准 WinForm 应用程序示例)-前 2 种工作正常,我对第 3 种有问题:

1. datagridview.DataSource = dataTable :它有效 所以我可以通过设置进行过滤: dataTable.DefaultView.RowFilter = "country LIKE '%s%'";

DataTable dt = new DataTable();

private void Form1_Load(object sender, EventArgs e)

    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[]  1, "Belgium" );
    dt.Rows.Add(new object[]  2, "France" );
    dt.Rows.Add(new object[]  3, "Germany" );
    dt.Rows.Add(new object[]  4, "Spain" );
    dt.Rows.Add(new object[]  5, "Switzerland" );
    dt.Rows.Add(new object[]  6, "United Kingdom" );

    dataGridView1.DataSource = dt;


private void textBox1_TextChanged(object sender, EventArgs e)

    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());

    dt.DefaultView.RowFilter = string.Format("country LIKE '%0%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());
 

2。 datagridview.DataSource = bindingSource:它有效 所以我可以通过设置进行过滤: bindingSource.Filter = "country LIKE '%s%'";

DataTable dt = new DataTable();
BindingSource bs = new BindingSource();

private void Form1_Load(object sender, EventArgs e)

    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[]  1, "Belgium" );
    dt.Rows.Add(new object[]  2, "France" );
    dt.Rows.Add(new object[]  3, "Germany" );
    dt.Rows.Add(new object[]  4, "Spain" );
    dt.Rows.Add(new object[]  5, "Switzerland" );
    dt.Rows.Add(new object[]  6, "United Kingdom" );

    bs.DataSource = dt;
    dataGridView1.DataSource = bs;


private void textBox1_TextChanged(object sender, EventArgs e)

    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());

    bs.Filter = string.Format("country LIKE '%0%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());

3. datagridview.DataSource = 数据源; datagridview.DataMember = "TableName":它不起作用 当您使用设计器设计表格时会发生这种情况:将工具箱中的 DataSet 放在表单上,​​将 dataTable 添加到其中,然后设置 datagridview.DataSource = dataSource;和 datagridview.DataMember = "TableName"。 下面的代码假装这些操作:

DataSet ds = new DataSet();
DataTable dt = new DataTable();

private void Form1_Load(object sender, EventArgs e)

    dt.Columns.Add("id", typeof(int));
    dt.Columns.Add("country", typeof(string));

    dt.Rows.Add(new object[]  1, "Belgium" );
    dt.Rows.Add(new object[]  2, "France" );
    dt.Rows.Add(new object[]  3, "Germany" );
    dt.Rows.Add(new object[]  4, "Spain" );
    dt.Rows.Add(new object[]  5, "Switzerland" );
    dt.Rows.Add(new object[]  6, "United Kingdom" );

    ds.Tables.Add(dt);
    dataGridView1.DataSource = ds;
    dataGridView1.DataMember = dt.TableName;


private void textBox1_TextChanged(object sender, EventArgs e)

    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString());  
    //it is not working
    ds.Tables[0].DefaultView.RowFilter = string.Format("country LIKE '%0%'", textBox1.Text);

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString());

如果您对其进行测试 - 尽管数据表已被过滤(ds.Tables[0].DefaultView.Count 更改),但 datagridview 不会更新... 我一直在寻找任何解决方案,但问题是 DataSource 无法更改 - 因为它是额外的控制,我不希望它弄乱程序员的代码。

我知道可能的解决方案是: - 使用 DataBinding 从 DataSet 绑定 DataTable 并将其用作示例 2:但在代码编写期间由程序员决定, - 以编程方式将 dataSource 更改为 BindingSource、dataGridView.DataSource = dataSet.Tables[0] 或 DefaultView:但是,它会更改 DataSource。所以解决方案:

private void textBox1_TextChanged(object sender, EventArgs e)

    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    DataView dv = ds.Tables[0].DefaultView;
    dv.RowFilter = string.Format("country LIKE '%0%'", textBox1.Text);
    dataGridView1.DataSource = dv;

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

不可接受,正如您在 MessageBox 的数据源上看到的那样正在改变......

我不想这样做,因为程序员可能会编写类似这样的代码:

private void textBox1_TextChanged(object sender, EventArgs e)

    MessageBox.Show("DataSource type BEFORE = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    DataSet dsTmp = (DataSet)(dataGridView1.DataSource);   //<--- it is OK 

    DataView dv = ds.Tables[0].DefaultView;
    dv.RowFilter = string.Format("country LIKE '%0%'", textBox1.Text);
    dataGridView1.DataSource = dv;   //<--- here the source is changeing from DataSet to DataView

    MessageBox.Show("DataSource type AFTER = " + dataGridView1.DataSource.GetType().ToString(), ds.Tables[0].DefaultView.Count.ToString());

    dsTmp = (DataSet)(dataGridView1.DataSource);    //<-- throws an exception: Unable to cast object DataView to DataSet

他可以做到这一点,因为他在设计器中使用 DataSet 和 DataMember 设计了 ​​DataGridView。 代码会被编译,但是使用过滤器后会抛出异常...

所以问题是:如何过滤 DataSet 中的 DataTable 并在 DataGridView 上显示结果而不将 DataSource 更改为另一个?为什么我可以直接从示例 1 中过滤 DataTable,而从 DataSet 中过滤 DataTable 不起作用? 在这种情况下,也许它不是绑定到 DataGridView 的 DataTable ?

请注意,我的问题来自设计问题,因此解决方案必须适用于示例 3。

【问题讨论】:

我的 2 美分,以及所有有价值的 cmets 和解决方案。这是一个article,它描述了以这种方式过滤数据绑定 DataGridView 的优缺点,并为您提供了一些如何更好地做到这一点的想法。 请原谅重复,但我认为我的建议并非每次都有效。确实,有时会引发异常,而我的代码不太可能。尝试使用 bindingSource 进行过滤,您将有机会编写好的代码。喜欢日期:bindingSource.Filter = string.Format..... 我喜欢 TecMan 的评论。您可以通过 filter 属性将过滤工作委托给 IBindingListView 接口(工作较少,但仅可与 ADO.Net Datatable 一起使用)或在您的控件中完成整个工作(更多工作,但应该适用于任何事情)。 【参考方案1】:

我刚刚花了一个小时来解决类似的问题。对我来说,答案非常简单。

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '0'", textBoxFilter.Text);

【讨论】:

如何将此事件绑定到文本框 过滤语法可以在这里找到:csharp-examples.net/dataview-rowfilter 使用 DataTable 作为源解决了必须按照 msdn.microsoft.com/en-us/library/… 实现 IBindingListView 的问题 我收到此错误:Object reference not set to an instance of an object. 用于 GridView。 你的数据源是什么?我的示例假设您使用的是 DataTable。如果您正在使用其他东西,请检查您的铸件。在我的示例中为“作为 DataTable”。【参考方案2】:

我开发了一个通用语句来应用过滤器:

string rowFilter = string.Format("[0] = '1'", columnName, filterValue);
(myDataGridView.DataSource as DataTable).DefaultView.RowFilter = rowFilter;

方括号允许列名中包含空格。

此外,如果您想在过滤器中包含多个值,您可以为每个附加值添加以下行:

rowFilter += string.Format(" OR [0] = '1'", columnName, additionalFilterValue);

【讨论】:

【参考方案3】:

一种更简单的方法是横穿数据,并使用Visible 属性隐藏行。

// Prevent exception when hiding rows out of view
CurrencyManager currencyManager = (CurrencyManager)BindingContext[dataGridView3.DataSource];
currencyManager.SuspendBinding();

// Show all lines
for (int u = 0; u < dataGridView3.RowCount; u++)

    dataGridView3.Rows[u].Visible = true;
    x++;


// Hide the ones that you want with the filter you want.
for (int u = 0; u < dataGridView3.RowCount; u++)

    if (dataGridView3.Rows[u].Cells[4].Value == "The filter string")
    
        dataGridView3.Rows[u].Visible = true;
    
    else
    
        dataGridView3.Rows[u].Visible = false;
    


// Resume data grid view binding
currencyManager.ResumeBinding();

只是一个想法......它对我有用。

【讨论】:

作为手动填充DataGridView 的人,这非常有效。 :) 虽然我使用了foreach 并直接分配了row.Visible = showAll || &lt;condition&gt;; 而没有任何if。如果过滤器字符串为空,则 showAll 为真。 好主意,因为在这种情况下,我们不受数据源类型的限制。也没有任何数据表。 完美运行,为了改进搜索逻辑,我们可以将 if 条件替换为 dataGridView3.Rows[u].Cells[4].Value.ToString().IndexOf("The filter string" )>=0 使用visibleproperty 过滤可能会很慢。但是有一个很好的解决方法可以加快速度:***.com/a/69098980/9134997【参考方案4】:

对于那些已经实施检查答案但仍然收到错误的人

(对象引用未设置为对象的实例)

如cmets中提到的,可能DataGridView的数据源不是DataTable类型,如果是的话,再尝试将数据表分配给DataGridView的数据源。 就我而言,我将数据表分配给 FormLoad() 中的 DataGridView 当我写这段代码时

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '0'", textBoxFilter.Text);

它给了我上面提到的错误。因此,我再次将数据表重新分配给 dgv。所以代码类似于

dataGridViewFields.DataSource = Dt;
(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '0'", textBoxFilter.Text);

它奏效了。

【讨论】:

【参考方案5】:

您可以从您的数据源创建一个DataView 对象。这将允许您在不直接修改数据源的情况下过滤和排序数据。

另外,设置好数据源后记得调用dataGridView1.DataBind();

【讨论】:

感谢您的回答。是的,可以创建 DataView 对象,但是它会更改 DataSource 类型,请参阅最后的代码。我已经在上一篇文章中修改了我想避免这种情况的原因。 dataGridView1.DataBind() 方法在 WinForms 中不存在,我想它来自 ASP。【参考方案6】:

//“评论”过滤数据网格而不改变数据集,完美工作。

            (dg.ItemsSource as ListCollectionView).Filter = (d) =>
            
                DataRow myRow = ((System.Data.DataRowView)(d)).Row;
                if (myRow["FName"].ToString().ToUpper().Contains(searchText.ToString().ToUpper()) || myRow["LName"].ToString().ToUpper().Contains(searchText.ToString().ToUpper()))
                    return true; //if want to show in grid
                return false;    //if don't want to show in grid
            ;         

【讨论】:

【参考方案7】:

我对 DataGridView 中的自动搜索有更清晰的建议

这是一个例子

private void searchTb_TextChanged(object sender, EventArgs e)
    
        try
        
            (lecteurdgview.DataSource as DataTable).DefaultView.RowFilter = String.IsNullOrEmpty(searchTb.Text) ?
                "lename IS NOT NULL" :
                String.Format("lename LIKE '0' OR lecni LIKE '1' OR ledatenais LIKE '2' OR lelieu LIKE '3'", searchTb.Text, searchTb.Text, searchTb.Text, searchTb.Text);
        
        catch (Exception ex) 
            MessageBox.Show(ex.StackTrace);
        
    

【讨论】:

可能与***.com/questions/5843537/…重复【参考方案8】:

我找到了解决该问题的简单方法。在你刚刚完成的绑定 datagridview 时:datagridview.DataSource = dataSetName.Tables["TableName"];

如果你的代码是这样的:

datagridview.DataSource = dataSetName;
datagridview.DataMember = "TableName";

过滤时datagridview永远不会再次加载数据。

【讨论】:

以上是关于在不更改数据源的情况下过滤 DataGridView的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Group By / 有函数的情况下过滤 SQL 中的数据

如何在不使用 Perl 循环的情况下过滤数组?

在不使用保护子句的情况下过滤 erlang ets 表

如何在没有 Shiny 的情况下过滤 Rmarkdown 中的预聚合数据?

在 WPF 中,您可以在没有代码的情况下过滤 CollectionViewSource 吗?

在没有 DOM 的情况下过滤内存中的 XML 节点?