Filtrado de DataGridView sin cambiar la fuente de datos

Resuelto mj82 asked hace 13 años • 8 respuestas

Estoy desarrollando un control de usuario en C# Visual Studio 2010, una especie de cuadro de texto de "búsqueda rápida" para filtrar datagridview. Debería funcionar para 3 tipos de fuentes de datos de datagridview: DataTable, DataBinding y DataSet. Mi problema es filtrar DataTable desde el objeto DataSet, que se muestra en DataGridView.

Podría haber 3 casos (ejemplos para la aplicación WinForm estándar con DataGridView y TextBox): los primeros 2 funcionan bien, tengo problemas con el tercero:

1. datagridview.DataSource = dataTable: funciona
para poder filtrar configurando: dataTable.DefaultView.RowFilter = "país 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 = vinculanteSource: funciona
para poder filtrar configurando: vinculanteSource.Filter = "país 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 = fuente de datos; datagridview.DataMember = "TableName": no funciona Sucede
cuando diseñas una tabla usando el diseñador: coloca el conjunto de datos de la caja de herramientas en el formulario, agrégale dataTable y luego configura datagridview.DataSource = dataSource; y datagridview.DataMember = "NombreTabla".
El siguiente código simula estas operaciones:

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

Si lo prueba, aunque la tabla de datos está filtrada (ds.Tables[0].DefaultView.Count cambia), la vista de cuadrícula de datos no se actualiza... He estado buscando una solución durante mucho tiempo, pero el problema es que DataSource no puede cambiar : como es control adicional, no quiero que arruine el código del programador.

Sé que las posibles soluciones son:
- vincular DataTable desde DataSet usando DataBinding y usarlo como ejemplo 2: pero depende del programador durante la escritura del código,
- cambiar dataSource a BindingSource, dataGridView.DataSource = dataSet.Tables[0], o a DefaultView mediante programación: sin embargo, cambia el DataSource. Entonces la solución:

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

no es aceptable, como puede ver en el origen de datos de MessageBox está cambiando...

No quiero hacer eso, porque es posible que un programador escriba un código similar a este:

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
}

Puede hacerlo, ya que diseñó DataGridView con DataSet y DataMember en Designer. El código se compilará, sin embargo, después de usar un filtro, se generará una excepción...

Entonces la pregunta es: ¿cómo puedo filtrar DataTable en DataSet y mostrar los resultados en DataGridView sin cambiar DataSource a otro? ¿Por qué puedo filtrar DataTable del ejemplo 1 directamente, mientras que filtrar DataTable desde DataSet no funciona? ¿Quizás no esté DataTable vinculado a DataGridView en ese caso?

Tenga en cuenta que mi problema surge de problemas de diseño, por lo que la solución DEBE FUNCIONAR en el ejemplo 3.

mj82 avatar May 01 '11 00:05 mj82
Aceptado

Acabo de pasar una hora en un problema similar. Para mí la respuesta resultó ser vergonzosamente simple.

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
Brad Bruce avatar Apr 06 '2012 22:04 Brad Bruce

Desarrollé una declaración genérica para aplicar el filtro:

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

Los corchetes permiten espacios en el nombre de la columna.

Además, si desea incluir varios valores en su filtro, puede agregar la siguiente línea para cada valor adicional:

rowFilter += string.Format(" OR [{0}] = '{1}'", columnName, additionalFilterValue);
Joe Sisk avatar Aug 15 '2013 20:08 Joe Sisk

Para aquellos de ustedes que han implementado la respuesta marcada pero aún reciben el error

(Referencia a objeto no establecida como instancia de un objeto)

Como se mencionó en los comentarios, tal vez la fuente de datos del DataGridView no sea del tipo DataTable, pero si lo es, intente asignar la tabla de datos a la fuente de datos del DataGridView nuevamente. En mi caso, asigné la tabla de datos al DataGridView en FormLoad() y cuando escribo este código

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

me estaba dando el error que mencioné anteriormente. Entonces, reasigné la tabla de datos al dgv nuevamente. Entonces el código era algo así como

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

Y funcionó.

Rua'a avatar Jan 11 '2021 18:01 Rua'a