在不更改数据源的情况下过滤 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 || <condition>;
而没有任何if
。如果过滤器字符串为空,则 showAll
为真。
好主意,因为在这种情况下,我们不受数据源类型的限制。也没有任何数据表。
完美运行,为了改进搜索逻辑,我们可以将 if 条件替换为 dataGridView3.Rows[u].Cells[4].Value.ToString().IndexOf("The filter string" )>=0
使用visible
property 过滤可能会很慢。但是有一个很好的解决方法可以加快速度:***.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 中的数据
如何在没有 Shiny 的情况下过滤 Rmarkdown 中的预聚合数据?