Filtrer DataGridView sans modifier la source de données

95

Je développe le contrôle utilisateur dans C # Visual Studio 2010 - une sorte de zone de texte "recherche rapide" pour filtrer les vues de données. 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 de l'objet DataSet, qui est affiché sur DataGridView.

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

1. datagridview.DataSource = dataTable: cela fonctionne
pour que je puisse filtrer en définissant: 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: il fonctionne
donc je peux filtrer en définissant: 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": cela ne fonctionne pas
Cela se produit lorsque vous concevez une table à l'aide du designer: mettez le DataSet de la boîte à outils sur formulaire, ajoutez-y dataTable puis définissez datagridview.DataSource = dataSource; et datagridview.DataMember = "TableName".
Le 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 cherche depuis longtemps une solution, mais le problème est que DataSource ne peut pas change - comme il s'agit d'un contrôle supplémentaire, je ne veux pas qu'il gâche le code du programmeur.

Je sais que les solutions possibles sont les suivantes:
- lier DataTable à partir de DataSet à l'aide de DataBinding et l'utiliser comme exemple 2: mais c'est au programmeur lors de l'écriture de code,
- de changer dataSource en BindingSource, dataGridView.DataSource = dataSet.Tables [0], ou à DefaultView par programme: cependant, 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 dataSource de MessageBox est en train de changer ...

Je ne veux pas faire cela, car il est possible qu'un programmeur écrive un code similaire à celui-ci:

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èvera une exception ...

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

Veuillez noter que mon problème provient des problèmes de conception, donc la solution DOIT FONCTIONNER sur l'exemple 3.

mj82
la source
1
Mes 2 cents en plus de tous les précieux commentaires et solutions. Voici un article qui décrit les avantages et les inconvénients du filtrage de DataGridView lié aux données de cette façon et vous donne quelques idées pour mieux le faire.
TecMan
Excusez la répétition mais je pense que ma proposition ne fonctionne pas à chaque fois. En effet, parfois une exception est levée, ce que mon code est peu probable. En essayant de filtrer avec une bindingSource, vous avez toutes les chances de créer du bon code. Comme date: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD
J'aime le commentaire de TecMan. Vous pouvez déléguer le travail de filtrage à l'interface IBindingListView par la propriété de filtre (moins de travaux mais uniquement vraiment utilisable avec ADO.Net Datatable) ou faire tout le travail dans votre contrôle (plus de travaux mais devrait fonctionner avec n'importe quoi).
Marco Guignard

Réponses:

144

Je viens de passer une heure sur un problème similaire. Pour moi, la réponse s'est avérée extrêmement simple.

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
Brad Bruce
la source
2
comment mettre lier cet événement à textbox
Arun Prasad ES
7
La syntaxe de filtrage peut être trouvée ici: csharp-examples.net/dataview-rowfilter
Sal
L'utilisation d'un DataTable comme source permet de contourner le problème d'avoir à implémenter IBindingListViewselon msdn.microsoft.com/en-us/library
Jeremy Thompson
J'obtiens cette erreur: Object reference not set to an instance of an object.pour le GridView.
Si8
Quelle est votre source de données? Mon exemple suppose que vous utilisez un DataTable. Si vous utilisez autre chose, vérifiez votre diffusion. "as DataTable" dans mon exemple.
Brad Bruce
22

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);
Joe Sisk
la source
12

Un moyen plus simple consiste à traverser les données et à masquer les lignes avec la Visiblepropriété.

// 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.

João Moreira
la source
En tant que personne qui remplit manuellement un DataGridView, cela a parfaitement fonctionné. :) Bien que j'utilise un foreachet directement assigné row.Visible = showAll || <condition>;sans aucun if. Cela showAllest vrai si la chaîne de filtre est vide.
Andrew le
excellente idée car dans ce cas nous ne sommes pas liés au type de source de données. ni aucun DataTable.
mshakurov le
Fonctionne parfaitement, et pour améliorer la logique de recherche, nous pouvons remplacer la condition if par dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("The filter string")> = 0
Ali Ali
1

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.

N'oubliez pas d'appeler dataGridView1.DataBind();après avoir défini la source de données.

epotter
la source
2
Merci de votre réponse. Oui, l'objet DataView peut être créé, mais il modifie le type de DataSource, veuillez consulter le dernier code. J'ai modifié la raison pour laquelle je veux éviter cela dans le post précédent. La méthode dataGridView1.DataBind () n'existe pas dans WinForms, je suppose qu'elle provient d'ASP.
mj82
0

// "Comment" Filtrer la grille de données sans changer l'ensemble 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
            };         
PPr
la source
0

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);
        }
    }
KOUAKEP ARNOLD
la source
Peut dupliquer avec stackoverflow.com/questions/5843537/…
Tony Dong
-2

J'ai trouvé un moyen simple de résoudre ce problème. Lors de la liaison de datagridview, vous venez de faire:datagridview.DataSource = dataSetName.Tables["TableName"];

Si vous codez comme:

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

le datagridview ne chargera plus jamais les données lors du filtrage.

Quyền Đặng Quang
la source