eugenes
eugenes

Reputation: 151

How to filter DataGridView using TextBox

I don't have much experience yet. I'm trying to filter rows which contain the input text. In order.. I use MailKit to receive incoming messages and put data from them in Grid

public void ShowMessages(ImapClient client)
{
    var inbox = client.Inbox;
    inbox.Open(FolderAccess.ReadOnly);
    var query = SearchQuery.All;                                                                             
    var uids = inbox.Search(query);
    var items = inbox.Fetch(uids, MessageSummaryItems.Full | MessageSummaryItems.BodyStructure).Reverse();
    foreach (var item in items)
    {
       ...                                 
       dataGridMessages.Rows.Add(read, item.Envelope.Subject, item.Envelope.From, item.Envelope.Date, attach, item.Size, false, item.UniqueId);
    }
}

Then I try to filter the rows

private void searchTxb_TextChanged(object sender, EventArgs e)
{
    BindingSource bs = new BindingSource();
    bs.DataSource = dataGridMessages.DataSource;
    bs.Filter = string.Format("Subject LIKE '%{0}%'", searchTxb.Text);
    dataGridMessages.DataSource = bs;
}

but after typing the text the grid is empty, finds nothing. Please tell me what I'm doing wrong?

Upvotes: 1

Views: 240

Answers (1)

Steve
Steve

Reputation: 216243

Your problem is caused by the fact that you fill the grid one row at time adding directly a row. In this way, the DataSource property is not set to anything and if you want to filter you need to loop over the rows one by one and remove the unwanted rows.

In alternative you could use the BindingList<T> where T is a custom class that you define in your code with only the properties that you want to be displayed in the grid. With this class defined you can create the instances to add to the List used in the BindingList constructor

So suppose to have a class like this

private class MailItem
{
    public string Subject {get;set;}
    public string From {get;set;}
    public DateTime DateSent {get;set;}
}

and a global variable inside your form of type

List<MailItem> rows = new List<MailItem>();

For simplicity I have added just some properties, but you can add the others easily.
Now when you need to fill the grid use this

...
rows.Clear();
var items = inbox.Fetch(uids, MessageSummaryItems.Full | MessageSummaryItems.BodyStructure).Reverse()
foreach(var item in items)
{
    rows.Add(new MailItem {
       Subject = item.Envelope.Subject,
       From = item.Envelope.From,
       DateSent = item.Envelope.Date     
    };
}

Now you can create the BindingList and set it as the DataSource of the grid

BindingList<MailItem> bs = new BindingList<MailItem>(rows);
dataGridMessages.DataSource = bs;

The last step is the filtering code. Here you need to extract the rows you want to use and apply the filter

BindingList<MailItem> currentFilter = new BindingList<MailItem>(rows);
dataGridMessages.DataSource = currentFilter.Where(x => x.Subject.Contains(searchTxb.Text)).ToList();

Note that you need to preserve the original set of rows retrieved by the call to Fetch in such a way that you can apply a different filter while your user changes the textbox content. So, the list of MailItem should be kept global for your form

In alternative you can recall Fetch again, but it seems that this method is pretty slow even for just 100 elements to retrieve. A better model (IMHO) is to implement a button and call the filter on that button click.

(BindingList requires using System.ComponentModel)

Upvotes: 1

Related Questions