web-dev-qa-db-fra.com

Filtrer DataGridView sans changer de source de données

Je développe le contrôle utilisateur dans C # Visual Studio 2010 - une sorte de zone de texte de "recherche rapide" pour le filtrage de datagridview. Cela devrait fonctionner pour 3 types de sources de données datagridview: DataTable, DataBinding et DataSet . Mon problème concerne le filtrage de DataTable à partir d'un objet DataSet, affiché sur DataGridView.

Il peut y avoir 3 cas (exemples pour une application WinForm standard avec DataGridView et TextBox) - les 2 premiers fonctionnent correctement, j'ai un problème avec le 3ème:

1. datagridview.DataSource = dataTable: ça marche
Je peux donc filtrer en définissant: dataTable.DefaultView.RowFilter = "pays 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: ça marche
Je peux donc filtrer en définissant: bindingSource.Filter = "pays 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": cela ne fonctionne pas
Cela se produit lorsque vous concevez une table à l'aide de designer: placez le DataSet à partir de la boîte à outils sur le formulaire, ajoutez-lui dataTable, puis définissez datagridview.DataSource = dataSource; et datagridview.DataMember = "Nom de la table".
Code ci-dessous prétend ces opérations: 

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 vous le testez - bien que datatable soit filtré (ds.Tables [0] .DefaultView.Count change), datagridview n'est pas mis à jour ... Je cherchais depuis longtemps une solution, mais le problème est que cette DataSource ne peut pas changer - car c'est un contrôle supplémentaire, je ne veux pas que ça gâche le code du programmeur. 

Je sais que les solutions possibles sont:
- pour lier DataTable à partir de DataSet à l'aide de DataBinding et l'utiliser comme exemple 2: mais cela revient au programmeur lors de l'écriture du code,
- pour changer dataSource en BindingSource, dataGridView.DataSource = dataSet.Tables [0], ou en DefaultView par programme: toutefois, il modifie le DataSource. Donc la 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());
}

n'est pas acceptable, comme vous le voyez sur la source de données de MessageBox change ... 

Je ne veux pas faire cela, car il est possible qu'un programmeur écrit un code similaire à ceci:

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
}

Il peut le faire, car il a conçu DataGridView avec DataSet et DataMember dans le concepteur ... Le code sera compilé, cependant, après avoir utilisé un filtre, il lève une exception ...

La question est donc: comment puis-je filtrer DataTable dans DataSet et afficher les résultats sur DataGridView sans changer de source de données? Pourquoi je peux filtrer DataTable de l'exemple 1 directement, alors que filtrer DataTable de DataSet ne fonctionne pas? Peut-être que DataTable n'est pas lié à DataGridView dans ce cas? 

Veuillez noter que mon problème tient à la conception de problèmes, de sorte que la solution DOIT TRAVAILLER à l'exemple 3. 

84
mj82

Je viens de passer une heure sur un problème similaire. Pour moi, la réponse s'est avérée être d'une simplicité embarrassante.

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

J'ai développé une déclaration générique pour appliquer le filtre:

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

Les crochets permettent des espaces dans le nom de la colonne.

De plus, si vous souhaitez inclure plusieurs valeurs dans votre filtre, vous pouvez ajouter la ligne suivante pour chaque valeur supplémentaire:

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

Un moyen plus simple consiste à traverser les données et à masquer les lignes avec la propriété 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();

Juste une idée ... ça marche pour moi.

5
João Moreira

Vous pouvez créer un objet DataView à partir de votre source de données. Cela vous permettrait de filtrer et de trier vos données sans modifier directement la source de données.

Pensez également à appeler dataGridView1.DataBind(); après avoir défini la source de données.

1
epotter

J'ai une proposition plus claire sur la recherche automatique dans un DataGridView

ceci est un exemple

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);
        }
    }
0
KOUAKEP ARNOLD

// "Commentaire" Filtrer la grille de données sans changer le jeu de données, fonctionne parfaitement.

            (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
SPr