Filter data grid view list with the value from the search text box: 'Object reference not set to an instance of an object.'

怎甘沉沦 提交于 2019-12-08 18:38:28

Since you didn't set DataSource of DataGridView and it's null, then you received an exception when you used it in this statement: (memberGrid.DataSource as DataTable).DefaultView....

Instead of adding rows one by one to the DataGridView, assign data to its DataSource. Then depending to the data structure which you are using, you can filter data.

You can use either of the following solutions:

  • As an option, to fix the problem, you can load data into a DataTable and set it as DataSource of the DataGridView. Then the filtering can be done the same way that you are trying to do.

  • Also if for any reason you prefer to have a List<Member> and apply filter on that, after loading data and converting to a List<Member>, keep the list in a form member field like List<Member> members; then for filtering using linq:

    dataGridView1.DataSource = members.Where(x=>x.Name ==searchBox.Text).ToList();
    

For more details read the following sections in the post.

Why I receive NullReferenceException

The following line of code will throw a NullReferenceException if DataSource is null or DataSource is not a DataTable:

(memberGrid.DataSource as DataTable).DefaultView.RowFilter = ...

In your case, You are filling the database by adding rows and didn't used its data source, so DataSource is null and above code throws exception.

To find more information about how to debug NullReferenceException, take a look at What is a NullReferenceException, and how do I fix it?.

Load Data

You have mentioned:

This is how I add each row of data from my SQL select into the list.

members.Add(new Member {
      id = Convert.ToInt32(reader["id"]),
      name = reader["name"].ToString(), 
      ...

It seems you are using a SQL query and a SqlDataReader. Then you don't need to use a List<Member>, a DataTable would be enough for you. You can change your code to the following code to load data:

public DataTable GetData()
{
    var dt = new DataTable();
    var cn = @"Your Connection String";
    var cmd = @"Your Select Command";
    using (var da = new SqlDataAdapter(cmd, cn))
        da.Fill(dt);
    return dt;
}

Note: If for any reason you are interested to keep working with List<Member>, refactor your code to return a List<Member> in GetData:

public List<Member> GetData()
{
    var dt = new DataTable();
    var cn = @"Your Connection String";
    var cmd = @"Your Select Command";
    using (var da = new SqlDataAdapter(cmd, cn))
        da.Fill(dt);
    return dt.AsEnumerable().AsEnumerable().Select(r=>{
        id = r.Field<int>("id"),
        name = r.Field<string>("name"),
        age = r.Field<int>("age")
    }).ToList();
}

Show Data in DataGridView

You have mentioned:

To add to the grid, I loop through the list and add a row per member:

for (int i = 0; i < members.Count; i++) {
    memberGrid.Rows.Add(new object[]
      ...

When you load data into a DataTable (or even in a List<Member>) you don't need to add rows one by one to the DataGridView, just assign the data to DataSource property of the DataGridView.

To do so, you can override OnLoad method of the form or handle Load event and load data into a data table and set it as data source of the DataGridView:

DataTable dt;
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    dt = LoadData();
    dataGridView1.DataSource = dt;
}

Note: If for any reason you preferred returning a List<Member> from GetData, the code will be:

List<Member> members;
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    members = LoadData();
    dataGridView1.DataSource = members;
}

Filter Data

Then to filter data, it's enough to use dt.DefaultView.RowFilter:

dt.DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);

Note: If for any reason you prefer to have a List<Member> and apply filter on that, after loading and keep the list in a form member like what I did in above sections, use linq to filter data:

dataGridView1.DataSource = members.Where(x=>x.Name ==searchBox.Text).ToList();

Reza Aghaei explained how to solve problem but in case someone in future need maybe one more way to handle these situations here is how i do it.

So as mentioned problem is making (memberGrid.DataSource as DataTable).DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);

There are 2 reasons why it may happen.

  • You DataSource is not assigned so it is null
  • You assigned object with other type than DataTable and then you try to cast it "back" to DataTable so it returns null

For flawless flow ALWAYS bind data to data source and manipulate it through it. Here is how i am doing it in my datagridviews.

  • First i try to load data directly into DataTable

So if you are loading it from SQL just use DataAdapter and fill DataTable

using(SqlConnetion....)
{
    con.Open();
    using(SqlDataAdapter da ....)
    {
        DataTable dt = new DataTable();
        da.Fill(dt);

        dataGridView.DataSource = dt;

        //I use this method often because every change i am doing is directly into database and then i just refresh dgv if i need it by loading data again.
    }
}

If loading data directly do DataTable is for some reason hard for you (need to create datatable yourself and populate it with foreach but too many columns or something like that) or you want to manipulate data inside your code but not in DataTable you can use List<T>

So simply create object

public class Car
{
    public string Model { get; set; }
    public Color Color { get; set; }
    public string LicencePlate { get; set; }
    public doube HP { get; set; }

    public Car()
    {
    }
}

then let's say we need to load cars from jsonString we do List<Car> myCars = JsonConverter.Deserialize<List<Car>>(stringFromWebForExample);

and now we have got list in which we can manipulate but need to put it into datagridview. Again we could do it just by dataGridView.DataSource = myCars but DataTable is more functional than List when it comes to displaying in datagridview so what i do is convert that list to datatable with this code:

public static DataTable ConvertToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection properties =
       TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    return table;
}

and just call it dataGridView.DataSource = myCars.ConvertToDataTable();

So that are two practical ways of handing data and displaying it in DataGridView

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!