问题
I have set up a text box to search names on my data grid but I am receiving the error: 'Object reference not set to an instance of an object.'
List<Member> members = new List<Member>();
public class Member
{
public int id { get; set; }
public string name { get; set; }
public int age { get; set; }
public Image image_url { get; set; }
}
// In a keyup event of the text box
(memberGrid.DataSource as DataTable).DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);
I have attempted to change DataTable
to List
or Member
. I have also tried casting using List/Member
in front of DataTable
but it doesn't seem to work.
What's the best approach to this?
Edit:
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(),
age = Convert.ToInt32(reader["age"]),
image_url = (Image)Properties.Resources.ResourceManager.GetObject(reader["image_url"].ToString())
});
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[]
{
members[i].image_url,
members[i].name,
members[i].age
});
}
回答1:
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 asDataSource
of theDataGridView
. 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 aList<Member>
, keep the list in a form member field likeList<Member> members;
then for filtering usinglinq
: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();
回答2:
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 itis null
- You assigned object with other type than
DataTable
and then you try to cast it "back" toDataTable
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
来源:https://stackoverflow.com/questions/52237243/filter-data-grid-view-list-with-the-value-from-the-search-text-box-object-refe