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() + "%'";
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:
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();
}