Search code examples
c#vb.netwinformsdatatablerowfilter

How to filter items with DataTable.DefaultView.RowFilter


I have listbox in my project which is bind to datasource. I would like to be able to filter items only to items matches text i type in additional textbox control. I knew that i might use following DefaultView.RowFilter property as shown below in my old vb.net project. Nevertheless when i try to do the same in my most recent visual studio C# project seems like DefaultView is not anymore recognized. Any thoughts if this was decommised or any other way around how could i filter items?

listbox1.DataSource.DefaultView.RowFilter = "[Nummer] like '%" & lbSearchProd.Text.Trim()

Edit For @dr.null

I've added following line nevertheless i am getting the error below. Note that Datasource contains items.

(cbClient.DataSource as DataTable).DefaultView.RowFilter = "[Value] like '%" + txtFilterClient.Text.Trim() + "%'";

enter image description here

This is how i bind the control:

public void LoadDataToClients(IEnumerable<IdValueReadModel> clients)
{
    cbClient.DataSource = clients.ToList();
    cbClient.DisplayMember = "Value";
    cbClient.ValueMember = "Id";
}

Data is there:

enter image description here


Solution

  • Clarifying the comments to your question above.

    First of all

    listbox1.DataSource.DefaultView.RowFilter = "[Nummer] like '%" & lbSearchProd.Text.Trim()
    

    This line works in your old vb.net project because you have had the Option Strict Off which allows implicit narrowing conversions, late binding, and implicit typing that results in an object type. You should not allow the mentioned if you want to write good vb.net code and avoid possible mistakes and bugs by doing so. In c#, you don't have that option. You need to cast the object to the type of the assigned object instance to access it.

    Second of all, your data source here is a List<T> not DataTable. They are two different things and you can't apply what can be written for one to another. You need Linq to filter a List<T>.

    Some suggested solutions.


    DataTable
    Get a DataTable from your data access. Something like:

    // SqlClient example...
    private DataTable GetDataTable()
    {
        var dt = new DataTable();
    
        using (var con = new SqlConnection("...."))
        using (var cmd = new SqlCommand("Select...", con))
        using (var da = new SqlDataAdapter(cmd))
        {
            con.Open();
            da.Fill(dt);
        }
    
        return dt;
    }
    

    Bind it:

    private void LoadClient()
    {
        cbClient.DisplayMember = "Value";
        cbClient.ValueMember = "Id";
        cbClient.DataSource = GetDataTable();
    }
    

    Handle the TextBox.TextChanged to filter or remove the filter:

    private void txtFilterClient_TextChanged(object sender, EventArgs e)
    {
        if (cbClient.DataSource is DataTable dt)
        {
            var str = txtFilterClient.Text.Trim();
    
            dt.DefaultView.RowFilter = string.IsNullOrEmpty(str)
                ? null
                : $"Value LIKE '%{str}%'";
        }
    }
    


    BindingSource
    Like the first solution, get a DataTable but bind the control to the multipurpose BindingSource.

    private void LoadClient()
    {
        cbClient.DisplayMember = "Value";
        cbClient.ValueMember = "Id";
        cbClient.DataSource = new BindingSource(GetDataTable(), null);
    }
    
    private void txtFilterClient_TextChanged(object sender, EventArgs e)
    {
        if (cbClient.DataSource is BindingSource bs)
        {
            var str = txtFilterClient.Text.Trim();
    
            bs.Filter = string.IsNullOrEmpty(str)
                ? null
                : $"Value LIKE '%{str}%'";
        }
    }
    


    List<T>
    If you need to manipulate a List<T> and you have a model like:

    public class IdValueReadModel
    {
        public int Id { get; set; }
        public string Value { get; set; }
    }
    

    Then you can execute Linq queries to filter the data source which it's here of type List<IdValueReadModel>. You need to cache the original list in a class field to be the data source when you clear the filter. Run a Linq query to bind a new filtered list.

    private List<IdValueReadModel> clientCache;
    
    private void LoadClient()
    {
        clientCache = // Get/assign the list...
        cbClient.DisplayMember = "Value";
        cbClient.ValueMember = "Id";
        cbClient.DataSource = clientCache;
    }
    
    private void txtFilterClient_TextChanged(object sender, EventArgs e)
    {
        var str = txtFilterClient.Text.Trim().ToLower();
    
        cbClient.DataSource = string.IsNullOrEmpty(str)
            ? clientCache
            : clientCache
            .Where(c => c.Value.ToLower().Contains(str))
            .ToList();
    }