Filtrar DataGridView sin cambiar la fuente de datos

95

Estoy desarrollando control de usuario en C # Visual Studio 2010, una especie de cuadro de texto de "búsqueda rápida" para filtrar la vista de cuadrícula de datos. Debería funcionar para 3 tipos de fuentes de datos de datagridview: DataTable, DataBinding y DataSet. Mi problema es filtrar DataTable del 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 un problema con el tercero:

1. datagridview.DataSource = dataTable: funciona
así que puedo 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 = bindingSource: funciona,
así que puedo filtrar estableciendo: 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": no funciona
Sucede cuando diseña una tabla usando el diseñador: coloque el DataSet de la caja de herramientas en el formulario, agregue dataTable y luego configure datagridview.DataSource = dataSource; y datagridview.DataMember = "TableName".
El código siguiente 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 durante mucho tiempo alguna solución, pero el problema es que DataSource no puede cambiar - ya que es un control adicional, no quiero que se estropee con el código del programador.

Sé que las posibles soluciones son:
- enlazar 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 programáticamente: 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 DataSource 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
}

Él puede hacer eso, ya que diseñó DataGridView con DataSet y DataMember en el diseñador. El código se compilará, sin embargo, después de usar un filtro, arrojará 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 de DataSet no funciona? ¿Quizás no está DataTable vinculado a DataGridView en ese caso?

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

mj82
fuente
1
Mis 2 centavos además de todos los valiosos comentarios y soluciones. Aquí hay un artículo que describe los pros y los contras de filtrar DataGridView enlazado a datos de esta manera y le brinda algunas ideas sobre cómo hacerlo mejor.
TecMan
Disculpe la repetición pero creo que mi propuesta no siempre funciona. De hecho, a veces se levanta una excepción, lo que mi código es poco probable. Al intentar filtrar con un bindingSource, tiene todas las posibilidades de hacer un buen código. Como fecha: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD
Me gusta el comentario de TecMan. Puede delegar el trabajo de filtrado a la interfaz IBindingListView mediante la propiedad del filtro (menos trabajos, pero solo realmente se puede usar con ADO.Net Datatable) o hacer todo el trabajo bajo su control (más trabajos pero debería funcionar con cualquier cosa).
Marco Guignard

Respuestas:

144

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
fuente
2
cómo poner vincular este evento al cuadro de texto
Arun Prasad ES
7
La sintaxis de filtrado se puede encontrar aquí: csharp-examples.net/dataview-rowfilter
Sal
El uso de DataTable como fuente soluciona el problema de tener que implementar IBindingListViewsegún msdn.microsoft.com/en-us/library/…
Jeremy Thompson
Recibo este error: Object reference not set to an instance of an object.para GridView.
Si8
¿Cuál es tu fuente de datos? Mi ejemplo asume que está utilizando un DataTable. Si está usando algo más, revise su yeso. "como DataTable" en mi ejemplo.
Brad Bruce
23

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
fuente
12

Una forma más sencilla es traspasar los datos y ocultar las líneas con la Visiblepropiedad.

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

Solo una idea ... funciona para mí.

João Moreira
fuente
Como alguien que está rellenando manualmente un DataGridView, esto funcionó perfectamente. :) Aunque usé un foreachy asignado directamente row.Visible = showAll || <condition>;sin ninguno if. Eso showAlles cierto si la cadena de filtro está vacía.
Andrew
gran idea porque en este caso no estamos atados al tipo de fuente de datos. ni ningún DataTable.
mshakurov
Funcionó perfectamente y para mejorar la lógica de búsqueda podemos reemplazar la condición if para que sea dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("La cadena de filtro")> = 0
Ali Ali
1

Puede crear un objeto DataView desde su fuente de datos. Esto le permitiría filtrar y ordenar sus datos sin modificar directamente la fuente de datos.

Además, recuerde llamar dataGridView1.DataBind();después de configurar la fuente de datos.

epotter
fuente
2
Gracias por su respuesta. Sí, se puede crear un objeto DataView, sin embargo, cambia el tipo de fuente de datos, consulte el último código. Modifiqué la razón por la que quiero evitar eso en una publicación anterior. El método dataGridView1.DataBind () no existe en WinForms, supongo que es de ASP.
mj82
0

// "Comentario" Filtra la cuadrícula de datos sin cambiar el conjunto de datos, funciona perfectamente.

            (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
            };         
PPr
fuente
0

Tengo una propuesta más clara sobre la búsqueda automática en un DataGridView

esto es un ejemplo

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);
        }
    }
KOUAKEP ARNOLD
fuente
Puede duplicarse con stackoverflow.com/questions/5843537/…
Tony Dong
-2

Encontré una forma sencilla de solucionar ese problema. En el enlace datagridview, acaba de hacer:datagridview.DataSource = dataSetName.Tables["TableName"];

Si codifica como:

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

la vista de cuadrícula de datos nunca volverá a cargar datos al filtrar.

Quyền Đặng Quang
fuente