การกรอง DataGridView โดยไม่ต้องเปลี่ยนแหล่งข้อมูล


98

ฉันกำลังพัฒนาการควบคุมผู้ใช้ใน C # Visual Studio 2010 ซึ่งเป็นช่องข้อความ "ค้นหาอย่างรวดเร็ว" สำหรับการกรอง datagridview ควรใช้กับแหล่งข้อมูล datagridview 3 ประเภท ได้แก่ DataTable, DataBinding และ DataSet ปัญหาของฉันคือการกรอง DataTable จากวัตถุ DataSet ซึ่งแสดงบน DataGridView

อาจมี 3 กรณี (ตัวอย่างสำหรับแอปพลิเคชัน WinForm มาตรฐานที่มี DataGridView และ TextBox อยู่) - 2 รายการแรกทำงานได้ดีฉันมีปัญหากับอันที่ 3:

1. datagridview.DataSource = dataTable: มันใช้งานได้
ดังนั้นฉันจึงสามารถกรองโดยการตั้งค่า: dataTable.DefaultView.RowFilter = "ประเทศ 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: มันใช้งานได้
ดังนั้นฉันจึงสามารถกรองโดยการตั้งค่า: bindingSource.Filter = "ประเทศ 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": มันใช้ไม่ได้
มันเกิดขึ้นเมื่อคุณออกแบบตารางโดยใช้ตัวออกแบบ: ใส่ DataSet จากกล่องเครื่องมือในแบบฟอร์มเพิ่ม dataTable เข้าไปแล้วตั้งค่า datagridview.DataSource = dataSource; และ datagridview.DataMember = "TableName"
โค้ดด้านล่างหลอกว่าการดำเนินการเหล่านี้:

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

หากคุณทดสอบ - แม้ว่า datatable จะถูกกรอง (ds.Tables [0] .DefaultView.Count changes) datagridview ไม่ได้รับการอัปเดต ... ฉันมองหาวิธีแก้ปัญหามานานแล้ว แต่ปัญหาคือDataSource ไม่สามารถ เปลี่ยน - เนื่องจากเป็นการควบคุมเพิ่มเติมฉันไม่ต้องการให้มันยุ่งกับรหัสของโปรแกรมเมอร์

ฉันรู้วิธีแก้ปัญหาที่เป็นไปได้คือ
- เพื่อผูก DataTable จาก DataSet โดยใช้ DataBinding และใช้เป็นตัวอย่างที่ 2: แต่ขึ้นอยู่กับโปรแกรมเมอร์ในระหว่างการเขียนโค้ด
- เพื่อเปลี่ยน dataSource เป็น BindingSource, dataGridView.DataSource = dataSet.Tables [0] หรือ เป็น DefaultView โดยทางโปรแกรมอย่างไรก็ตามจะเปลี่ยน DataSource ดังนั้นวิธีแก้ปัญหา:

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

ไม่เป็นที่ยอมรับดังที่คุณเห็นใน dataSource ของ MessageBox กำลังเปลี่ยนแปลง ...

ฉันไม่ต้องการทำเช่นนั้นเพราะเป็นไปได้ที่โปรแกรมเมอร์จะเขียนโค้ดในลักษณะนี้:

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
}

เขาทำได้ในขณะที่เขาออกแบบ DataGridView ด้วย DataSet และ DataMember ในดีไซน์เนอร์ โค้ดจะถูกคอมไพล์อย่างไรก็ตามหลังจากใช้ตัวกรองแล้วจะมีข้อยกเว้น ...

คำถามคือ: ฉันจะกรอง DataTable ใน DataSet และแสดงผลลัพธ์บน DataGridView โดยไม่เปลี่ยน DataSource เป็นอื่นได้อย่างไร เหตุใดฉันจึงสามารถกรอง DataTable จากตัวอย่างที่ 1 ได้โดยตรงในขณะที่การกรอง DataTable จาก DataSet ไม่ทำงาน อาจจะไม่ใช่ DataTable ที่ผูกไว้กับ DataGridView ในกรณีนั้น?

โปรดทราบว่าปัญหาของฉันเกิดจากปัญหาในการออกแบบดังนั้นวิธีแก้ปัญหาต้องใช้ในตัวอย่างที่ 3


1
2 เซ็นต์ของฉันนอกเหนือจากความคิดเห็นและแนวทางแก้ไขที่มีค่าทั้งหมด นี่คือบทความที่อธิบายข้อดีข้อเสียของการกรอง DataGridView แบบผูกข้อมูลด้วยวิธีนี้และให้แนวคิดบางประการเกี่ยวกับวิธีการทำให้ดีขึ้น
TecMan

แก้ตัวซ้ำซาก แต่ฉันคิดว่าข้อเสนอของฉันไม่ได้ผลทุกครั้ง บางครั้งมีการยกข้อยกเว้นซึ่งรหัสของฉันไม่น่าเป็นไปได้ พยายามกรองด้วย BindingSource คุณมีโอกาสที่จะสร้างรหัสที่ดีทุกครั้ง Like date: bindingSource.Filter = string.Format .....
KOUAKEP ARNOLD

ฉันชอบความคิดเห็นของ TecMan คุณสามารถมอบหมายงานการกรองไปยังอินเทอร์เฟซ IBindingListView โดยคุณสมบัติตัวกรอง (ใช้งานได้น้อยลง แต่ใช้ได้กับ ADO.Net Datatable เท่านั้น) หรือทำงานทั้งหมดในการควบคุมของคุณ (ทำงานได้มากกว่า แต่ควรทำงานกับอะไรก็ได้)
Marco Guignard

คำตอบ:


148

ฉันใช้เวลาหนึ่งชั่วโมงกับปัญหาที่คล้ายกัน สำหรับฉันคำตอบกลับกลายเป็นเรื่องง่ายอย่างน่าอาย

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

2
วิธีผูกเหตุการณ์นี้กับกล่องข้อความ
อรุณประสาท ES

7
ไวยากรณ์การกรองสามารถพบได้ที่นี่: csharp-examples.net/dataview-rowfilter
Sal

การใช้ DataTable เป็นแหล่งที่มาทำให้เกิดปัญหาในการต้องใช้งานIBindingListViewตามmsdn.microsoft.com/en-us/library/…
Jeremy Thompson

ฉันได้รับข้อผิดพลาดนี้: Object reference not set to an instance of an object.สำหรับ GridView
Si8

แหล่งข้อมูลของคุณคืออะไร? ตัวอย่างของฉันสันนิษฐานว่าคุณกำลังใช้ DataTable หากคุณกำลังใช้อย่างอื่นให้ตรวจสอบการแคสต์ของคุณ "as DataTable" ในตัวอย่างของฉัน
Brad Bruce

23

ฉันพัฒนาคำสั่งทั่วไปเพื่อใช้ตัวกรอง:

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

วงเล็บเหลี่ยมอนุญาตให้มีช่องว่างในชื่อคอลัมน์

นอกจากนี้หากคุณต้องการรวมหลายค่าในตัวกรองของคุณคุณสามารถเพิ่มบรรทัดต่อไปนี้สำหรับค่าเพิ่มเติมแต่ละค่า:

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

12

วิธีที่ง่ายกว่าคือการขวางข้อมูลและซ่อนเส้นด้วย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();

แค่ความคิด ... มันได้ผลสำหรับฉัน


ในฐานะคนที่เติม a ด้วยตนเองสิ่งDataGridViewนี้จึงทำงานได้อย่างสมบูรณ์แบบ :) ถึงแม้ว่าผมจะใช้foreachและได้รับมอบหมายโดยตรงโดยไม่ต้องมีrow.Visible = showAll || <condition>; ifนั่นshowAllคือความจริงถ้าสตริงตัวกรองว่างเปล่า
แอนดรูว์

ความคิดที่ดีเพราะในกรณีนี้เราไม่ได้เชื่อมโยงกับประเภทของแหล่งข้อมูล หรือ DataTable ใด ๆ
mshakurov

ทำงานได้อย่างสมบูรณ์แบบและเพื่อปรับปรุงตรรกะการค้นหาเราสามารถแทนที่เงื่อนไข if เป็น dataGridView3.Rows [u] .Cells [4] .Value ToString (). IndexOf ("สตริงตัวกรอง")> = 0
Ali Ali

1

คุณสามารถสร้างวัตถุDataViewจากแหล่งข้อมูลของคุณ วิธีนี้จะช่วยให้คุณสามารถกรองและจัดเรียงข้อมูลของคุณโดยไม่ต้องแก้ไขแหล่งข้อมูลโดยตรง

นอกจากนี้อย่าลืมโทรdataGridView1.DataBind();หลังจากที่คุณตั้งค่าแหล่งข้อมูลแล้ว


2
ขอบคุณสำหรับคำตอบ. ใช่ออบเจ็กต์ DataView สามารถสร้างได้ แต่จะเปลี่ยนประเภทแหล่งข้อมูลโปรดดูรหัสสุดท้าย ฉันได้แก้ไขสาเหตุที่ฉันต้องการหลีกเลี่ยงสิ่งนั้นในโพสต์ก่อนหน้านี้ dataGridView1.DataBind () วิธีการไม่มีอยู่ใน WinForms ฉันคิดว่ามันมาจาก ASP
mj82

0

// "แสดงความคิดเห็น" กรองข้อมูลโดยไม่ต้องเปลี่ยนชุดข้อมูลทำงานได้อย่างสมบูรณ์แบบ

            (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

ฉันมีข้อเสนอที่ชัดเจนกว่าเกี่ยวกับการค้นหาอัตโนมัติใน DataGridView

นี่คือตัวอย่าง

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

ฉันพบวิธีง่ายๆในการแก้ไขปัญหานั้น ในการรวมข้อมูลมุมมองที่คุณเพิ่งทำ:datagridview.DataSource = dataSetName.Tables["TableName"];

หากคุณเขียนโค้ดเช่น:

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

datagridview จะไม่โหลดข้อมูลอีกเลยเมื่อทำการกรอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.