Filtraggio di DataGridView senza modificare l'origine dati


95

Sto sviluppando il controllo utente in C # Visual Studio 2010 - una sorta di casella di testo "ricerca rapida" per filtrare datagridview. Dovrebbe funzionare per 3 tipi di origini dati datagridview: DataTable, DataBinding e DataSet. Il mio problema è con il filtraggio di DataTable dall'oggetto DataSet, che viene visualizzato su DataGridView.

Potrebbero esserci 3 casi (esempi per l'applicazione WinForm standard con DataGridView e TextBox su di esso) - i primi 2 funzionano bene, ho problemi con il terzo:

1. datagridview.DataSource = dataTable: funziona
quindi posso filtrare impostando: 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: funziona
quindi posso filtrare impostando: 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": non funziona
Succede quando si progetta una tabella utilizzando designer: inserire il DataSet da toolbox nel form, aggiungere dataTable ad esso e quindi impostare datagridview.DataSource = dataSource; e datagridview.DataMember = "TableName".
Il codice seguente finge queste operazioni:

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

Se lo provi, sebbene datatable sia filtrato (ds.Tables [0] .DefaultView.Count cambia), datagridview non viene aggiornato ... Ho cercato a lungo qualsiasi soluzione, ma il problema è che DataSource non può modificare - poiché è un controllo aggiuntivo, non voglio che rovini il codice del programmatore.

So che le possibili soluzioni sono:
- associare DataTable da DataSet utilizzando DataBinding e usarlo come esempio 2: ma spetta al programmatore durante la scrittura del codice,
- modificare dataSource in BindingSource, dataGridView.DataSource = dataSet.Tables [0], o a DefaultView programmaticamente: tuttavia, cambia il DataSource. Quindi la soluzione:

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

non è accettabile, come vedi su dataSource di MessageBox sta cambiando ...

Non voglio farlo, perché è possibile che un programmatore scriva codice simile a questo:

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
}

Può farlo, poiché ha progettato DataGridView con DataSet e DataMember in designer. Il codice verrà compilato, tuttavia, dopo aver utilizzato un filtro, verrà generata un'eccezione ...

Quindi la domanda è: come posso filtrare DataTable in DataSet e mostrare i risultati su DataGridView senza cambiare DataSource in un altro? Perché posso filtrare DataTable dall'esempio 1 direttamente, mentre il filtro DataTable da DataSet non funziona? Forse non è DataTable associato a DataGridView in quel caso?

Si noti che il mio problema deriva dai problemi di progettazione, quindi la soluzione DEVE FUNZIONARE sull'esempio 3.


1
I miei 2 centesimi oltre a tutti i preziosi commenti e soluzioni. Di seguito è riportato un articolo che descrive i pro ei contro del filtro DataGridView associato a dati in questo modo e fornisce alcune idee su come farlo meglio.
TecMan

Scusa la ripetizione ma penso che la mia proposta non funzioni ogni volta. In effetti, a volte viene revocata un'eccezione, cosa improbabile dal mio codice. Cercando di filtrare con un bindingSource hai tutte le possibilità di creare un buon codice. Come la data: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD

Mi piace il commento di TecMan. È possibile delegare il lavoro di filtraggio all'interfaccia IBindingListView tramite la proprietà del filtro (meno funziona ma solo realmente utilizzabile con ADO.Net Datatable) o eseguendo l'intero lavoro nel controllo (più lavori ma dovrebbe funzionare con qualsiasi cosa).
Marco Guignard

Risposte:


143

Ho appena passato un'ora su un problema simile. Per me la risposta si è rivelata semplice in modo imbarazzante.

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

2
come mettere associare questo evento alla casella di testo
Arun Prasad ES

7
La sintassi del filtro può essere trovata qui: csharp-examples.net/dataview-rowfilter
Sal

L'utilizzo di una DataTable come fonte aggira il problema di dover implementare IBindingListViewsecondo msdn.microsoft.com/en-us/library/…
Jeremy Thompson

Ottengo questo errore: Object reference not set to an instance of an object.per GridView.
Si8

Qual è la tua fonte di dati? Il mio esempio presuppone che tu stia utilizzando un DataTable. Se stai usando qualcos'altro, controlla il tuo casting. "come DataTable" nel mio esempio.
Brad Bruce

22

Ho sviluppato una dichiarazione generica per applicare il filtro:

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

Le parentesi quadre consentono spazi nel nome della colonna.

Inoltre, se desideri includere più valori nel filtro, puoi aggiungere la seguente riga per ogni valore aggiuntivo:

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

12

Un modo più semplice è attraversare i dati e nascondere le linee con la Visibleproprietà.

// 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 un'idea ... funziona per me.


Come qualcuno che sta compilando manualmente un DataGridView, questo ha funzionato perfettamente. :) Anche se ho usato un foreache assegnato direttamente row.Visible = showAll || <condition>;senza alcun if. Ciò showAllè vero se la stringa del filtro è vuota.
Andrew

ottima idea perché in questo caso non siamo legati al tipo di fonte dati. né alcun DataTable.
mshakurov

Ha funzionato perfettamente e per migliorare la logica di ricerca possiamo sostituire la condizione if in dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("The filter string")> = 0
Ali Ali

1

È possibile creare un oggetto DataView dalla propria origine dati. Ciò consentirebbe di filtrare e ordinare i dati senza modificare direttamente l'origine dati.

Inoltre, ricorda di chiamare dataGridView1.DataBind();dopo aver impostato l'origine dati.


2
Grazie per la risposta. Sì, è possibile creare l'oggetto DataView, tuttavia cambia il tipo di DataSource, vedere l'ultimo codice. Ho modificato il motivo per cui voglio evitarlo nel post precedente. Il metodo dataGridView1.DataBind () non esiste in WinForms, suppongo che provenga da ASP.
mj82

0

// "Comment" Filtra datagrid senza modificare il set di dati, funziona perfettamente.

            (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

Ho una proposta più chiara sulla ricerca automatica in un DataGridView

questo è un esempio

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


-2

Ho trovato un modo semplice per risolvere il problema. Al collegamento di datagridview hai appena fatto:datagridview.DataSource = dataSetName.Tables["TableName"];

Se codifichi come:

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

il datagridview non caricherà mai più i dati durante il filtraggio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.