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


95

我正在C#Visual Studio 2010中开发用户控件-一种用于过滤datagridview的“快速查找”文本框。它应该适用于3种类型的datagridview数据源:DataTable,DataBinding和DataSet。我的问题是从DataSetView上显示的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 = 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:但是,它更改了数据源。所以解决方案:

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的dataSource上看到的那样,这是不可接受的...

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

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。代码将被编译,但是,使用过滤器后,它将引发异常。

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

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


1
我的2美分除了所有有价值的评论和解决方案。这里有一个文章,描述的优点和过滤数据绑定的DataGridView这种方式的利弊,并为您提供了该怎么做更好的一些想法。
TecMan 2015年

请原谅,但我认为我的建议并非每次都能奏效。确实,有时会取消异常,而我的代码不太可能。尝试使用bindingSource进行过滤,您将有机会编写出良好的代码。像日期一样:bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD'Apr 17'17

我喜欢TecMan评论。您可以通过filter属性将过滤工作委托给IBindingListView接口(较少的工作,但只能与ADO.Net Datatable一起真正使用),也可以在控件中完成全部工作(更多的工作,但应该可以处理任何事情)。
Marco Guignard '18年

Answers:


143

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

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

2
如何将事件绑定到文本框
Arun Prasad ES

7
过滤语法可以在这里找到:csharp-examples.net/dataview-rowfilter
萨尔

使用DataTable作为源解决了必须IBindingListView按照msdn.microsoft.com/en-us/library/…
Jeremy Thompson

我收到此错误:Object reference not set to an instance of an object.对于GridView。
Si8

您的数据源是什么?我的示例假设您正在使用DataTable。如果您使用其他东西,请检查铸件。在我的示例中为“作为DataTable”。
布拉德·布鲁斯

22

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

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

方括号在列名称中允许有空格。

此外,如果要在过滤器中包含多个值,则可以为每个其他值添加以下行:

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

12

一种更简单的方法是横向显示数据,并隐藏带有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,这非常有效。:)尽管我使用了a foreachrow.Visible = showAll || <condition>;但没有任何直接分配if。这showAll是真实的,如果过滤字符串是空的。
安德鲁(Andrew)

好主意,因为在这种情况下,我们不依赖于数据源的类型。也没有任何DataTable。
mshakurov

完美运行,并且为了改善搜索逻辑,我们可以将if条件替换为dataGridView3.Rows [u] .Cells [4] .Value.ToString()。IndexOf(“ filter string”)> = 0
Ali Ali

1

您可以从数据源创建一个DataView对象。这将使您无需直接修改数据源即可对数据进行过滤和排序。

另外,记住dataGridView1.DataBind();在设置数据源后再打电话。


2
谢谢你的答案。是的,可以创建DataView对象,但是它将更改DataSource类型,请参见最后的代码。我已经修改了我在上一篇文章中希望避免的原因。在WinForms中不存在dataGridView1.DataBind()方法,我想它是来自ASP。
mj82

0

//“注释”在不更改数据集的情况下过滤datagrid,完全有效。

            (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
            };         

0

我对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);
        }
    }


-2

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

如果您的代码如下:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.