【问题标题】:Filtering DataGridView without changing datasource在不更改数据源的情况下过滤 DataGridView
【发布时间】: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 一起使用)或在您的控件中完成整个工作(更多工作,但应该适用于任何事情)。

标签: c# winforms visual-studio-2010 datagridview filter


【解决方案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 过滤可能会很慢。但是有一个很好的解决方法可以加快速度:stackoverflow.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);
                }
            }
        

        【讨论】:

        【解决方案8】:

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

        如果你的代码是这样的:

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

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

        【讨论】:

          猜你喜欢
          • 2014-05-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多