Lọc DataGridView mà không thay đổi nguồn dữ liệu


95

Tôi đang phát triển quyền kiểm soát người dùng trong C # Visual Studio 2010 - một loại hộp văn bản "tìm nhanh" để lọc chế độ xem dữ liệu. Nó sẽ hoạt động cho 3 loại nguồn dữ liệu datagridview: DataTable, DataBinding và DataSet. Vấn đề của tôi là lọc DataTable từ đối tượng DataSet, được hiển thị trên DataGridView.

Có thể có 3 trường hợp (ví dụ cho ứng dụng WinForm tiêu chuẩn với DataGridView và TextBox trên đó) - 2 trường hợp đầu tiên đang hoạt động OK, tôi gặp sự cố với trường hợp thứ 3:

1. datagridview.DataSource = dataTable: nó hoạt động
để tôi có thể lọc bằng cách thiết lập: 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 = bindSource: nó hoạt động
để tôi có thể lọc bằng cách thiết lập: bindSource.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": nó không hoạt động
Điều này xảy ra khi bạn thiết kế một bảng bằng trình thiết kế: đặt DataSet từ hộp công cụ trên biểu mẫu, thêm dataTable vào nó và sau đó đặt datagridview.DataSource = dataSource; và datagridview.DataMember = "TableName".
Mã dưới đây giả vờ các hoạt động này:

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

Nếu bạn kiểm tra nó - mặc dù dữ liệu được lọc (ds.Tables [0] .DefaultView.Count thay đổi), datagridview không được cập nhật ... Tôi đã tìm kiếm giải pháp trong một thời gian dài, nhưng vấn đề là DataSource không thể thay đổi - vì đó là quyền kiểm soát bổ sung, tôi không muốn nó làm xáo trộn mã của lập trình viên.

Tôi biết các giải pháp khả thi là:
- liên kết DataTable từ DataSet bằng DataBinding và sử dụng nó như ví dụ 2: nhưng tùy thuộc vào lập trình viên trong quá trình viết mã,
- thay đổi dataSource thành BindingSource, dataGridView.DataSource = dataSet.Tables [0], hoặc thành DefaultView theo chương trình: tuy nhiên, nó thay đổi DataSource. Vì vậy, giải pháp:

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

là không thể chấp nhận được, như bạn thấy trên dataSource của MessageBox đang thay đổi ...

Tôi không muốn làm điều đó, vì có thể một lập trình viên viết mã tương tự như thế này:

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
}

Anh ấy có thể làm điều đó, khi anh ấy thiết kế DataGridView với DataSet và DataMember trong trình thiết kế. Mã sẽ được biên dịch, tuy nhiên, sau khi sử dụng bộ lọc, nó sẽ ném ra một ngoại lệ ...

Vì vậy, câu hỏi đặt ra là: làm thế nào tôi có thể lọc DataTable trong DataSet và hiển thị kết quả trên DataGridView mà không thay đổi DataSource thành một nguồn khác? Tại sao tôi có thể lọc DataTable từ ví dụ 1 trực tiếp, trong khi lọc DataTable từ DataSet không hoạt động? Có thể nó không phải DataTable bị ràng buộc với DataGridView trong trường hợp đó?

Xin lưu ý rằng vấn đề của tôi lấy từ vấn đề thiết kế, vì vậy giải pháp PHẢI LÀM VIỆC ở ví dụ 3.


1
2 xu của tôi ngoài tất cả các nhận xét và giải pháp có giá trị. Đây là bài viết mô tả ưu và nhược điểm của việc lọc DataGridView có ràng buộc dữ liệu theo cách này và cung cấp cho bạn một số ý tưởng về cách làm điều đó tốt hơn.
TecMan

Xin lỗi vì sự lặp lại nhưng tôi nghĩ rằng đề xuất của tôi không hoạt động lần nào. Thật vậy, đôi khi một ngoại lệ được dỡ bỏ, điều mà mã của tôi không chắc. Cố gắng lọc với một bindingSource, bạn có mọi cơ hội để tạo mã tốt. Giống như ngày: bindSource.Filter = string.Format .....
KOUAKEP ARNOLD

Tôi thích bình luận của TecMan. Bạn có thể ủy thác công việc lọc cho giao diện IBindingListView theo thuộc tính bộ lọc (ít hoạt động hơn nhưng chỉ thực sự có thể sử dụng được với ADO.Net Datatable) hoặc thực hiện toàn bộ công việc trong sự kiểm soát của bạn (nhiều công việc hơn nhưng sẽ hoạt động với mọi thứ).
Marco Guignard

Câu trả lời:


144

Tôi vừa dành một giờ cho một vấn đề tương tự. Đối với tôi, câu trả lời trở nên đơn giản một cách đáng xấu hổ.

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

2
làm thế nào để đưa ràng buộc sự kiện này để textbox
Arun Prasad ES

7
Cú pháp lọc có thể được tìm thấy tại đây: csharp-examples.net/dataview-rowfilter
Sal

Sử dụng DataTable làm nguồn sẽ giải quyết được vấn đề phải triển khai IBindingListViewtheo msdn.microsoft.com/en-us/library/…
Jeremy Thompson

Tôi gặp lỗi này: Object reference not set to an instance of an object.đối với GridView.
Si8

Nguồn dữ liệu của bạn là gì? Ví dụ của tôi giả định rằng bạn đang sử dụng DataTable. Nếu bạn đang sử dụng thứ khác, hãy kiểm tra quá trình truyền của bạn. "as DataTable" trong ví dụ của tôi.
Brad Bruce

23

Tôi đã phát triển một tuyên bố chung để áp dụng bộ lọc:

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

Dấu ngoặc vuông cho phép khoảng trắng trong tên cột.

Ngoài ra, nếu bạn muốn bao gồm nhiều giá trị trong bộ lọc của mình, bạn có thể thêm dòng sau cho mỗi giá trị bổ sung:

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

12

Một cách đơn giản hơn là chuyển ngang dữ liệu và ẩn các dòng có thuộc Visibletính.

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

Chỉ là một ý tưởng ... nó phù hợp với tôi.


Là một người đang điền thủ công a DataGridView, điều này hoạt động hoàn hảo. :) Mặc dù tôi đã sử dụng một foreachvà được chỉ định trực tiếp row.Visible = showAll || <condition>;mà không có bất kỳ if. Điều đó showAllđúng nếu chuỗi bộ lọc trống.
Andrew

ý tưởng tuyệt vời bởi vì trong trường hợp này chúng ta không bị ràng buộc với loại nguồn dữ liệu. cũng không phải bất kỳ DataTable nào.
mshakurov

Hoạt động hoàn hảo và để cải thiện logic tìm kiếm, chúng tôi có thể thay thế điều kiện if thành dataGridView3.Rows [u] .Cells [4] .Value.ToString (). IndexOf ("Chuỗi bộ lọc")> = 0
Ali Ali

1

Bạn có thể tạo một đối tượng DataView từ nguồn dữ liệu của mình. Điều này sẽ cho phép bạn lọc và sắp xếp dữ liệu của mình mà không cần sửa đổi trực tiếp nguồn dữ liệu.

Ngoài ra, hãy nhớ gọi điện dataGridView1.DataBind();sau khi bạn đặt nguồn dữ liệu.


2
Cảm ơn bạn đã trả lời. Có, đối tượng DataView có thể được tạo, tuy nhiên nó thay đổi loại DataSource, vui lòng xem mã cuối cùng. Tôi đã sửa đổi lý do tại sao tôi muốn tránh điều đó trong bài viết trước. Phương thức dataGridView1.DataBind () không tồn tại trong WinForms, tôi cho rằng nó từ ASP.
mj82

0

// "Nhận xét" Lọc datagrid mà không thay đổi tập dữ liệu, Hoạt động hoàn hảo.

            (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

Tôi có một đề xuất rõ ràng hơn về tìm kiếm tự động trong DataGridView

đây là một ví dụ

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

Tôi đã tìm thấy một cách đơn giản để khắc phục vấn đề đó. Tại chế độ xem dữ liệu ràng buộc bạn vừa thực hiện:datagridview.DataSource = dataSetName.Tables["TableName"];

Nếu bạn viết mã như sau:

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

datagridview sẽ không bao giờ tải lại dữ liệu khi lọc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.