Filtern von DataGridView ohne Ändern der Datenquelle


95

Ich entwickle die Benutzersteuerung in C # Visual Studio 2010 - eine Art "Schnellsuche" -Textfeld zum Filtern der Datagrid-Ansicht. Es sollte für drei Arten von Datagridview-Datenquellen funktionieren: DataTable, DataBinding und DataSet. Mein Problem ist das Filtern von DataTable aus dem DataSet-Objekt, das in DataGridView angezeigt wird.

Es kann 3 Fälle geben (Beispiele für eine Standard-WinForm-Anwendung mit DataGridView und TextBox) - die ersten 2 funktionieren einwandfrei, ich habe ein Problem mit dem dritten:

1. datagridview.DataSource = dataTable: Es funktioniert,
damit ich filtern kann, indem ich Folgendes einstelle: 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: Es funktioniert,
damit ich filtern kann, indem ich Folgendes eingebe: 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": funktioniert nicht
Es passiert, wenn Sie eine Tabelle mit dem Designer entwerfen: Legen Sie das DataSet aus der Toolbox in ein Formular, fügen Sie dataTable hinzu und legen Sie dann datagridview.DataSource = dataSource fest. und datagridview.DataMember = "TableName".
Der folgende Code gibt diese Operationen vor:

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

Wenn Sie es testen - obwohl die Datentabelle gefiltert ist (ds.Tables [0] .DefaultView.Count-Änderungen), wird die Datagrid-Ansicht nicht aktualisiert ... Ich habe lange nach einer Lösung gesucht, aber das Problem ist, dass DataSource dies nicht kann ändern - als zusätzliche Kontrolle möchte ich nicht, dass es den Code des Programmierers durcheinander bringt.

Ich weiß, dass mögliche Lösungen sind:
- DataTable mit DataBinding aus DataSet zu binden und als Beispiel 2 zu verwenden: aber es liegt beim Programmierer beim Programmieren,
- DataSource in BindingSource zu ändern, dataGridView.DataSource = dataSet.Tables [0] oder programmgesteuert zu DefaultView: Die DataSource wird jedoch geändert. Also die Lösung:

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

ist nicht akzeptabel, wie Sie auf MessageBox sehen. Die Datenquelle ändert sich ...

Ich möchte das nicht tun, weil es möglich ist, dass ein Programmierer ähnlichen Code schreibt:

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
}

Er kann dies tun, da er DataGridView mit DataSet und DataMember im Designer entworfen hat. Code wird kompiliert, aber nach Verwendung eines Filters wird eine Ausnahme ausgelöst ...

Die Frage ist also: Wie kann ich DataTable in DataSet filtern und die Ergebnisse in DataGridView anzeigen, ohne DataSource in eine andere zu ändern? Warum kann ich DataTable direkt aus Beispiel 1 filtern, während das Filtern von DataTable aus DataSet nicht funktioniert? Vielleicht ist DataTable in diesem Fall nicht an DataGridView gebunden?

Bitte beachten Sie, dass mein Problem vom Entwerfen von Problemen herrührt, daher MUSS die Lösung in Beispiel 3 funktionieren.


1
Meine 2 Cent zusätzlich zu allen wertvollen Kommentaren und Lösungen. In diesem Artikel werden die Vor- und Nachteile des Filterns von datengebundenem DataGridView auf diese Weise beschrieben und einige Ideen zur besseren Vorgehensweise aufgeführt.
TecMan

Entschuldigen Sie die Wiederholung, aber ich denke, mein Vorschlag funktioniert nicht jedes Mal. In der Tat wird manchmal eine Ausnahme aufgehoben, was mein Code unwahrscheinlich ist. Wenn Sie versuchen, mit einer BindingSource zu filtern, haben Sie jede Chance, guten Code zu erstellen. Gefällt mir Datum: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD

Ich mag TecMan Kommentar. Sie können die Filterarbeit über die Filtereigenschaft an die IBindingListView-Schnittstelle delegieren (weniger funktioniert, aber nur mit ADO.Net Datatable wirklich verwendbar) oder die gesamte Arbeit in Ihrem Steuerelement ausführen (mehr funktioniert, sollte aber mit allem funktionieren).
Marco Guignard

Antworten:


143

Ich habe gerade eine Stunde mit einem ähnlichen Problem verbracht. Für mich war die Antwort peinlich einfach.

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

2
Wie man dieses Ereignis an ein Textfeld bindet
Arun Prasad ES


Durch die Verwendung einer Datentabelle als Quelle wird das Problem umgangen, dassIBindingListView gemäß msdn.microsoft.com/en-us/library/…
Jeremy Thompson

Ich erhalte diesen Fehler: Object reference not set to an instance of an object.für die GridView.
Si8

Was ist Ihre Datenquelle? In meinem Beispiel wird davon ausgegangen, dass Sie eine DataTable verwenden. Wenn Sie etwas anderes verwenden, überprüfen Sie Ihr Casting. "as DataTable" in meinem Beispiel.
Brad Bruce

22

Ich habe eine generische Anweisung entwickelt, um den Filter anzuwenden:

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

Die eckigen Klammern lassen Leerzeichen im Spaltennamen zu.

Wenn Sie mehrere Werte in Ihren Filter aufnehmen möchten, können Sie außerdem für jeden weiteren Wert die folgende Zeile hinzufügen:

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

12

Eine einfachere Möglichkeit besteht darin, die Daten zu durchqueren und die Linien mit der VisibleEigenschaft auszublenden .

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

Nur eine Idee ... es funktioniert bei mir.


Als jemand, der a manuell ausfüllt DataGridView, hat dies perfekt funktioniert. :) Obwohl ich ein verwendet habe foreachund direkt row.Visible = showAll || <condition>;ohne eines zugewiesen if. Das showAllist wahr, wenn die Filterzeichenfolge leer ist.
Andrew

Tolle Idee, denn in diesem Fall sind wir nicht an die Art der Datenquelle gebunden. noch irgendeine DataTable.
Mshakurov

Hat perfekt funktioniert und zur Verbesserung der Suchlogik können wir die if-Bedingung als dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("The filter string")> = 0
Ali Ali

1

Sie können ein DataView- Objekt aus Ihrer Datenquelle erstellen. Auf diese Weise können Sie Ihre Daten filtern und sortieren, ohne die Datenquelle direkt zu ändern.

Denken Sie auch daran, dataGridView1.DataBind();nach dem Einstellen der Datenquelle aufzurufen .


2
Danke für die Antwort. Ja, das DataView-Objekt kann erstellt werden. Es ändert jedoch den DataSource-Typ. Weitere Informationen finden Sie im letzten Code. Ich habe den Grund, warum ich das vermeiden möchte, im vorherigen Beitrag geändert. Die Methode dataGridView1.DataBind () ist in WinForms nicht vorhanden. Ich nehme an, sie stammt von ASP.
mj82

0

// "Kommentar" Datagrid filtern, ohne den Datensatz zu ändern. Funktioniert einwandfrei.

            (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

Ich habe einen klareren Vorschlag zur automatischen Suche in einer DataGridView

dies ist ein Beispiel

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

Ich habe einen einfachen Weg gefunden, um dieses Problem zu beheben. Beim Binden von Datagridview haben Sie gerade Folgendes getan:datagridview.DataSource = dataSetName.Tables["TableName"];

Wenn Sie wie folgt codieren:

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

Die Datagrid-Ansicht lädt beim Filtern nie wieder Daten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.