C# gives NullReferenceException while processing lists - c#

The following code gives a NullReferenceException while processing. Can anyone tell me why its happening, and how to fix it? thanks in advance! this might be a very simple thing, I'm missing somewhere.
if (a.Count != 0)
{
foreach(DataGridViewRow row in a )
{
foreach (DataGridViewRow newrow in b)
{
if( row.Cells[0].Value.ToString() == newrow.Cells[0].Value.ToString() &&
row.Cells[1].Value.ToString() == newrow.Cells[1].Value.ToString()) // this is the line that gives the error.
{
a.Remove(row);
}
}
}
}
the two lists have been declared at the top of the class so I don't know why its giving this error.
List<DataGridViewRow> a = new List<DataGridViewRow>();
List<DataGridViewRow> b = new List<DataGridViewRow>();
As suggested, I tried using a for loop bit it still gives the same exception
here's the code
if (a.Count != 0)
{
for (int i = a.Count - 1; i >= 0; i--)
{
int index1 = i;
for (int k = 0; k < b.Count; k++)
{
int index2 = k;
if (a.ElementAt<DataGridViewRow> (index1).Cells[0].Value.ToString() == b.ElementAt<DataGridViewRow>(index2).Cells[0].Value.ToString() && a.ElementAt<DataGridViewRow>(index1).Cells[1].Value.ToString() == b.ElementAt<DataGridViewRow>(index2).Cells[1].Value.ToString())
{
a.RemoveAt(index1);
}
else continue;
}
}

To find the null pointer exception, use the debugger. One of your variables is null.
But once that is fixed, you cannot modify a list while you are iterating over it. The simplest solution in the code you've presented is to change your foreach loops into for loops.
From the MSDN documentation for foreach:
The foreach statement repeats a group of embedded statements for each element in an array or an object collection. The foreach statement is used to iterate through the collection to get the desired information, but should not be used to change the contents of the collection to avoid unpredictable side effects.

You probably have a null Value, so ToString() fails.

A few possibilities:
row.Cells[0] is null
row.Cells[1] is null
row.Cells[0].Value is null
row.Cells[1].Value is null

You cannot delete an element from a collection that you are iterating through. The solution is to store the list of elements to be deleted in another List, and then delete them in another iteration. Following is a solution.
//New list that will contain the Objects to be deleted later on.
List<DataGridView> listToDelete = new List<DataGridView>();
if (a.Count != 0)
{
foreach(DataGridViewRow row in a )
{
foreach (DataGridViewRow newrow in b)
{
if( row.Cells[0].Value.ToString() == newrow.Cells[0].Value.ToString() &&
row.Cells[1].Value.ToString() == newrow.Cells[1].Value.ToString())
{
listToDelete.Add(row);
}
}
}
}
foreach (DataGridView d in listToDelete) {
a.Remove(d);
}

Related

Counting in lists with conditional statements

I know this is a common topic but I have been through most of the threads here on Stackoverflow and having followed them, can't see to get mine to satisfy all conditions.
I want to return the 2nd item in a list and if the list is null or only has 1 item in it, return 0;
I have this:
public int practice(List<int> items)
{
if (items == null)
{
return 0;
}
else if (items.Count == 1)
{
return 0;
}
else
{
int second_place = items[1];
return second_place;
}
}
I can't get this to work if the list has only 1 item in it. It just bypasses my else if condition and then fails.
I have tried items.Count and items.Count() but it doesn't seem to make a difference.
Instead of adding another else condition, you could just combine them as follows:
public int practice(List<int> items)
{
if (items == null || items.Count <= 1)
{
return 0;
}
else
{
int second_place = items[1];
return second_place;
}
}
Ok so I figured out what I wasn't doing correct. The code wasn't passing if the list had 0 items in it (but was not null).
So I added another else if statement to handle that:
else if (items.Count == 0)
{
return 0;
}
And then it passed. I didn't originally do this because I had not initially thought of the case where the list is not null but has 0 items in it. I was incorrectly thinking it had either a value of null or 1 item or greater.

How do I set specific columns in a DataGridView to 0 if it is blank?

In this example I am looping through each cell individually.
However, I have a name column and another optional column which I want to avoid. So I'd rather want to loop through a specific set of columns without the optional one instead but I'm not sure how.
This is how I did the thorough sweep:
foreach (DataGridViewRow row in DGVExcel.Rows)
{
for (int i = 0; i < row.Cells.Count; i++)
{
if (row.Cells[i].Value == null || row.Cells[i].Value == DBNull.Value ||
String.IsNullOrWhiteSpace(row.Cells[i].Value.ToString()))
{
row.Cells[i].Value = 0;
//DGVExcel.RefreshEdit();
}
}
}
However, I have a name column [...] so I'd rather'd loop through specific columns instead
If I understand you correctly you could get the index of the column and you can skip one for-loop:
int colindex = DGVExcel.Columns["SpecificColumnName"].Index;
foreach (var row in DGVExcel.Rows)
{
if (row.Cells[colindex].Value == null || row.Cells[colindex].Value == DBNull.Value ||
String.IsNullOrWhiteSpace(row.Cells[colindex].Value.ToString()))
{
row.Cells[colindex].Value = 0;
//DGVExcel.RefreshEdit();
}
}
EDIT
Is there a way to list the excluded columns instead of the included ones, because listing each one would be messy
In this case I would leave it with 2 for-loops. Basically you could save all the names in a List and check whether it contains the name of the current column and if it doesn't then you can do your 0 replacement.
List<string> ExcludedColumnsList = new List<string> { "ExcludedColumnName_1", "ExcludedColumnName_2" };
foreach (DataGridViewRow row in DGVExcel.Rows)
{
for (int i = 0; i < row.Cells.Count; i++)
{
if (!ExcludedColumnsList.Contains(DGVExcel.Columns[i].Name))
{
if (row.Cells[i].Value == null || row.Cells[i].Value == DBNull.Value ||
String.IsNullOrWhiteSpace(row.Cells[i].Value.ToString()))
{
row.Cells[i].Value = 0;
//DGVExcel.RefreshEdit();
}
}
}
}
Another option could also be to use linq. Get all indices except the excluded columns and foreach only through those:
List<string> ExcludedColumnsList = new List<string> { "ExcludedColumnName_1", "ExcludedColumnName_2" };
List<int> indexList = dataGridView1.Columns.Cast<DataGridViewColumn>()
.Where(x => !ExcludedColumnsList.Contains(x.Name))
.Select(x => x.Index).ToList();
foreach (DataGridViewRow row in DGVExcel.Rows)
{
foreach (int i in indexList)
{
if (row.Cells[i].Value == null || row.Cells[i].Value == DBNull.Value ||
String.IsNullOrWhiteSpace(row.Cells[i].Value.ToString()))
{
row.Cells[i].Value = 0;
//DGVExcel.RefreshEdit();
}
}
}

How can I iterate through an array and skip any empty items?

I have taken a csv, made from an excel file, and put it into a data table. There are cells from the excel csv file that are empty and when I iterate through them, they are also iterated through. I wish to not iterate over them.
foreach (DataRow datarow in sorted.Rows)
{
Boolean first = true;
Console.Write(Environment.NewLine);
foreach (var item in datarow.ItemArray)
{
if (item != null)
{
int i = 0;
if (first)
first = false;
else
Console.Write(",");
Console.Write(item);
}
else
break;
}
}
I have tried the above and it still iterates through the empty cells.
Expanding on JohnD answer, you can try this, assuming you only want to output the fields to the Console:
var text = string.Empty;
foreach (DataRow datarow in sorted.Rows)
{
var items = datarow.ItemArray
.Where(x => ((x != null) && !string.IsNullOrEmpty(x.ToString())));
var textJoined = string.Join(",", items);
text += textJoined + Environment.NewLine;
}
Console.WriteLine(text);
You may not be familiar with LINQ, so you will need the following using statement:
using System.Linq;
Again this solution assumes you only want to output the values to the console window, it does not assume you want to iterate through all the columns for a given row. If that is what you want let me know and I can make the appropriate modifications
[edit]
whoops just re-read your question and it appears you do want to iterate through each column, so here is a solution below:
var text = string.Empty;
foreach (DataRow datarow in sorted.Rows)
{
var items = datarow.ItemArray
.Where(x => ((x != null) && !string.IsNullOrEmpty(x.ToString())));
var currentLine = string.Empty;
foreach(var item in items)
{
// do something with item
// in this case append to currentLine
currentLine += item + ",";
}
text += currentLine.Substring(0, currentLine.Length - 2) + Environment.NewLine;
}
Console.WriteLine(text);
You get the same result, you can now just do what you need for each item
Assuming that the item is really a string, check to see if item is null or empty using String.IsNullOrEmpty().
if (item != null && !String.IsNullOrEmpty(item.ToString()))
Additionally, replace the break statement with a continue
Why not like this?
// ItemArray is object[]
foreach (var item in datarow.ItemArray)
{
if (first)
{
first = false;
}
else
{
// I think you are expecting to see the comma here even the previous element is empty e.g. A,B,,D (that missing C)
Console.Write(",");
}
// so here we cannot guarantee "if (item is string)"
if (item != null)
{
Console.Write(item.ToString());
}
}
(My habit to wrap all codes in {})
foreach (DataRow datarow in sorted.Rows)
{
Boolean first = true;
Console.Write(Environment.NewLine);
foreach (var item in datarow.ItemArray)
{
if (!string.IsNullOrEmpty((item ?? "").ToString()))
{
int i = 0;
if (first)
first = false;
else
Console.Write(",");
Console.Write(item);
}
else
continue;
}
}

Want to get total of same string values in a column of gridview

There is checkbox column in the gridview from which string value "P" is getting on checked. So, I want to calculate all rows of this column having same "P" values. I tried below code:
int sumP = 0;
public void countpresent() //To count all Present Students in the gridview
{
for (int i = 0; i < (dgvAttendance.Rows.Count-1); i++)
{
if (dgvAttendance.Rows[i].Cells["Present"].Value.ToString() == "P")
{
sumP += 1;
}
}
lblPresent.Text = sumP.ToString();
}
it is working for all the sting "P" but when it shows value null, it throws an exception "Object reference notset to an instance of an object". In the exception detail it is showing "NullReferenceException".
Please any one suggest something to help
You may want to check if the dgv row isn't null before you do your operation:
In C#6:
if (dgvAttendance.Rows[i].Cells["Present"].Value?.ToString() == "P")
{
sumP += 1;
}
Or in older version:
if (dgvAttendance.Rows[i].Cells["Present"].Value != null && dgvAttendance.Rows[i].Cells["Present"].Value?.ToString() == "P")
{
sumP += 1;
}
Or actually, in older version, you could try use Convert.ToString, since it is designed to handle null
if (Convert.ToString(dgvAttendance.Rows[i].Cells["Present"].Value) == "P")
{
sumP += 1;
}
It throws an exception because:
dgvAttendance.Rows[i].Cells["Present"].Value
is null and you can't do a .ToString() on a null value.
You can check that it's not null first:
if (dgvAttendance.Rows[i].Cells["Present"].Value != null &&
dgvAttendance.Rows[i].Cells["Present"].Value.ToString() == "P")
with C# 6 you can do this in a single line:
if (dgvAttendance.Rows[i].Cells["Present"].Value?.ToString() == "P")
The ? is short hand for the != null &&

Delete row in a DataGridView C#

I tried to loop through my dataGridView1 and remove rows which don't satisfy the condition as following:
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (!(Convert.ToDateTime(row.Cells[7].Value) - DateTime.Today).Days <= 0)
{
dataGridView1.Rows.Remove(row); //error: Uncommitted new row cannot be deleted.
}
}
But I got this error:
Uncommitted new row cannot be deleted.
I can manage if the code also VB.NET.
Don't use foreach in this case, the looped collection may be modified and leads to unpredicted result, sometimes throws exception like collection was modified (encountered mainly in LINQ), use for instead:
for(int i = dataGridView1.RowCount-1; i >= 0; i--){
var row = dataGridView1.Rows[i];
if (!row.IsNewRow&&!(Convert.ToDateTime(row.Cells[7].Value) - DateTime.Today).Days <= 0){
dataGridView1.Rows.Remove(row);
}
}
Note that we have to loop from the largest index to 0.
Try
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (!(row.Cells.OfType<DataGridViewCell>().All(c=>c.Value == null))
{
if (!(Convert.ToDateTime(row.Cells[7].Value) - DateTime.Today).Days <= 0)
{
dataGridView1.Rows.Remove(row);
}
}
}
try with putting following condition:
foreach(DataGridViewRow row in dataGridView1.Rows)
{
if(!row.IsNewRow)
{
if (!(Convert.ToDateTime(row.Cells[7].Value) - DateTime.Today).Days <= 0)
{
dataGridView1.Rows.Remove(row);
}
}
}

Categories