Memfilter DataGridView tanpa mengubah sumber data

95

Saya sedang mengembangkan kontrol pengguna di C # Visual Studio 2010 - semacam kotak teks "pencarian cepat" untuk memfilter datagridview. Ini harus bekerja untuk 3 jenis sumber data datagridview: DataTable, DataBinding dan DataSet. Masalah saya adalah dengan memfilter DataTable dari objek DataSet, yang ditampilkan di DataGridView.

Mungkin ada 3 kasus (contoh untuk aplikasi WinForm standar dengan DataGridView dan TextBox di atasnya) - 2 pertama berfungsi OK, saya bermasalah dengan yang ketiga:

1. datagridview.DataSource = dataTable: berfungsi
jadi saya bisa memfilter dengan menyetel: 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: berfungsi
sehingga saya dapat memfilter dengan menyetel: 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": tidak berfungsi
Ini terjadi saat Anda mendesain tabel menggunakan desainer: letakkan DataSet dari toolbox di formulir, tambahkan dataTable ke dalamnya, lalu setel datagridview.DataSource = dataSource; dan datagridview.DataMember = "TableName".
Kode di bawah menganggap operasi ini:

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

Jika Anda mengujinya - meskipun datatable difilter (ds.Tables [0] .DefaultView.Count berubah), datagridview tidak diperbarui ... Saya sudah lama mencari solusi apa pun, tetapi masalahnya adalah bahwa DataSource tidak bisa ubah - karena ini adalah kontrol tambahan, saya tidak ingin mengacaukan kode programmer.

Saya tahu solusi yang mungkin adalah:
- untuk mengikat DataTable dari DataSet menggunakan DataBinding dan menggunakannya sebagai contoh 2: tetapi terserah programmer selama penulisan kode,
- untuk mengubah dataSource ke BindingSource, dataGridView.DataSource = dataSet.Tables [0], atau ke DefaultView secara programatik: namun, ini mengubah Sumber Data. Jadi solusinya:

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

tidak dapat diterima, seperti yang Anda lihat di MessageBox's dataSource berubah ...

Saya tidak ingin melakukan itu, karena mungkin saja seorang programmer menulis kode yang mirip dengan ini:

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
}

Dia bisa melakukan itu, karena dia mendesain DataGridView dengan DataSet dan DataMember di desainer. Kode akan dikompilasi, namun, setelah menggunakan filter, itu akan memunculkan pengecualian ...

Jadi pertanyaannya adalah: bagaimana cara memfilter DataTable di DataSet dan menampilkan hasilnya di DataGridView tanpa mengubah DataSource ke yang lain? Mengapa saya dapat memfilter DataTable dari contoh 1 secara langsung, sementara memfilter DataTable dari DataSet tidak berfungsi? Mungkin itu tidak DataTable terikat ke DataGridView dalam kasus itu?

Harap dicatat, bahwa masalah saya berasal dari masalah desain, jadi solusinya HARUS BEKERJA pada contoh 3.

mj82
sumber
1
2 sen saya di samping semua komentar dan solusi yang berharga. Berikut adalah artikel yang menjelaskan pro dan kontra pemfilteran DataGridView yang terikat data dengan cara ini dan memberi Anda beberapa ide tentang cara melakukannya dengan lebih baik.
TecMan
Maafkan pengulangan tetapi saya pikir proposal saya tidak berhasil setiap saat. Memang, terkadang pengecualian dicabut, yang kode saya tidak mungkin. Mencoba memfilter dengan bindingSource, Anda memiliki setiap kesempatan untuk membuat kode yang baik. Seperti tanggal: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD
Saya suka komentar TecMan. Anda dapat mendelegasikan pekerjaan pemfilteran ke antarmuka IBindingListView dengan properti filter (lebih sedikit berfungsi tetapi hanya benar-benar dapat digunakan dengan ADO.Net Datatable) atau melakukan seluruh pekerjaan dalam kontrol Anda (lebih banyak pekerjaan tetapi harus bekerja dengan apa pun).
Marco Guignard

Jawaban:

144

Saya hanya menghabiskan satu jam untuk masalah yang sama. Bagi saya jawabannya ternyata sangat sederhana dan memalukan.

(dataGridViewFields.DataSource as DataTable).DefaultView.RowFilter = string.Format("Field = '{0}'", textBoxFilter.Text);
Brad Bruce
sumber
2
cara mengikat acara ini ke kotak teks
Arun Prasad ES
7
Sintaks pemfilteran dapat ditemukan di sini: csharp-examples.net/dataview-rowfilter
Sal
Menggunakan DataTable sebagai sumber mengatasi masalah harus mengimplementasikan IBindingListViewsesuai msdn.microsoft.com/en-us/library/…
Jeremy Thompson
Saya mendapatkan kesalahan ini: Object reference not set to an instance of an object.untuk GridView.
Si8
Apa sumber data Anda? Contoh saya mengasumsikan bahwa Anda menggunakan DataTable. Jika Anda menggunakan sesuatu yang lain, periksa transmisi Anda. "sebagai DataTable" dalam contoh saya.
Brad Bruce
23

Saya mengembangkan pernyataan umum untuk menerapkan filter:

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

Tanda kurung siku memungkinkan adanya spasi pada nama kolom.

Selain itu, jika Anda ingin menyertakan beberapa nilai dalam filter Anda, Anda dapat menambahkan baris berikut untuk setiap nilai tambahan:

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

Cara yang lebih sederhana adalah dengan melintang data, dan menyembunyikan garis dengan Visibleproperti.

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

Hanya sebuah ide ... itu berhasil untukku.

João Moreira
sumber
Sebagai seseorang yang mengisi a secara manual DataGridView, ini bekerja dengan sempurna. :) Meskipun saya menggunakan foreachdan langsung ditugaskan row.Visible = showAll || <condition>;tanpa if. Itu showAllbenar jika string filter kosong.
Andrew
ide bagus karena dalam hal ini kami tidak terikat dengan jenis sumber data. atau DataTable apa pun.
mshakurov
Bekerja dengan sempurna, dan untuk meningkatkan logika pencarian kita dapat mengganti kondisi if menjadi dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("The filter string")> = 0
Ali Ali
1

Anda bisa membuat objek DataView dari sumber data Anda. Ini akan memungkinkan Anda untuk memfilter dan menyortir data Anda tanpa mengubah sumber data secara langsung.

Selain itu, ingatlah untuk menelepon dataGridView1.DataBind();setelah Anda menyetel sumber data.

penulis cerita
sumber
2
Terimakasih untuk jawaban. Ya, objek DataView dapat dibuat, namun ia mengubah tipe DataSource, silakan lihat kode terakhir. Saya telah mengubah alasan mengapa saya ingin menghindarinya di posting sebelumnya. metode dataGridView1.DataBind () tidak ada di WinForms, saya kira itu dari ASP.
mj82
0

// "Komentar" Filter datagrid tanpa mengubah dataset, bekerja dengan sempurna.

            (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
sumber
0

Saya memiliki proposal yang lebih jelas tentang pencarian otomatis di DataGridView

ini sebuah contoh

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
sumber
Dapat diduplikasi dengan stackoverflow.com/questions/5843537/…
Tony Dong
-2

Saya menemukan cara sederhana untuk memperbaiki masalah itu. Di binding datagridview yang baru saja Anda lakukan:datagridview.DataSource = dataSetName.Tables["TableName"];

Jika Anda membuat kode seperti:

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

datagridview tidak akan pernah memuat data lagi saat memfilter.

Quyền Đặng Quang
sumber