developer tip

데이터 소스를 변경하지 않고 DataGridView 필터링

optionbox 2020. 9. 12. 09:47
반응형

데이터 소스를 변경하지 않고 DataGridView 필터링


저는 C # Visual Studio 2010에서 사용자 정의 컨트롤을 개발 중입니다. 즉, datagridview를 필터링하기위한 일종의 "빠른 찾기"텍스트 상자입니다. DataTable, DataBinding 및 DataSet의 세 가지 유형의 datagridview 데이터 소스에 대해 작동해야합니다. 내 문제는 DataGridView에 표시되는 DataSet 개체에서 DataTable을 필터링하는 것입니다.

3 가지 경우 (DataGridView 및 TextBox가있는 표준 WinForm 응용 프로그램의 예)가있을 수 있습니다. 처음 2 개는 정상적으로 작동하고 있으며 세 번째 경우에는 문제가 있습니다.

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을 추가 한 다음 set 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());
}

If you test it - although datatable is filtered (ds.Tables[0].DefaultView.Count changes), datagridview is not updated... I've been looking for a long time for any solution, but the problem is that DataSource cannot change - as it's additional control, I don't want it to mess up with programmer's code.

I know possible solutions are:
- to bind DataTable from DataSet using DataBinding and use it as example 2: but it's up to the programmer during code writing,
- to change dataSource to BindingSource, dataGridView.DataSource = dataSet.Tables[0], or to DefaultView programatically: however, it changes the DataSource. So the solution:

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

is not acceptable, as you see on MessageBox's dataSource is changing...

I don't want to do that, because it's possible a programmer writes code similar to this:

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
}

He can do that, as he designed DataGridView with DataSet and DataMember in designer. Code will be compiled, however, after using a filter, it will throw an exception...

So the question is: how can I filter DataTable in DataSet and show the results on DataGridView without changing DataSource to another? Why I can filter DataTable from example 1 directly, while filtering DataTable from DataSet is not working? Maybe it's not DataTable bound to DataGridView in that case?

Please note, that my problem takes from designing issues, so the solution MUST WORK on example 3.


I just spent an hour on a similar problem. For me the answer turned out to be embarrassingly simple.

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

I developed a generic statement to apply the filter:

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

The square brackets allow for spaces in the column name.

Additionally, if you want to include multiple values in your filter, you can add the following line for each additional value:

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

A simpler way is to transverse the data, and hide the lines with the Visible property.

// 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();

Just an idea... it works for me.


You could create a DataView object from your datasource. This would allow you to filter and sort your data without directly modifying the datasource.

Also, remember to call dataGridView1.DataBind(); after you set the data source.


//"Comment" Filter datagrid without changing dataset,Perfectly works.

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

I have a clearer proposal on automatic search in a DataGridView

this is an example

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

I found a simple way to fix that problem. At binding datagridview you've just done: datagridview.DataSource = dataSetName.Tables["TableName"];

If you code like:

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

the datagridview will never load data again when filtering.

참고URL : https://stackoverflow.com/questions/5843537/filtering-datagridview-without-changing-datasource

반응형