I want to click the "chkCPTData" button to delete some rows of the datagridview "CPTData". I have hundreds of rows of data in the datagridview. The first time I click the button, no rows are deleted. Then I click another time, some of the rows are deleted. It takes me about 8 times to delete all rows I want to delete. How can I can delete the rows by clicking the button only once? Thanks!
private void chkCPTData_Click(object sender, EventArgs e)
{
for (int rows = 0; rows <= CPTData.Rows.Count - 2; rows++)
{
double SampleDepth =(double)System.Convert.ToSingle(CPTData.Rows[rows].Cells[0].Value);
if (SampleDepth > (double)System.Convert.ToSingle(analysisDepth.Text))
{
CPTData.Rows.RemoveAt(rows);
}
}
CPTData.Refresh();
}
Deleting rows while enumerating through them will throw off the index, so try going in reverse instead:
for (int rows = CPTData.Rows.Count - 2; rows >=0; --rows)
{
double SampleDepth =(double)System.Convert.ToSingle(CPTData.Rows[rows].Cells[0].Value);
if (SampleDepth > (double)System.Convert.ToSingle(analysisDepth.Text))
{
CPTData.Rows.RemoveAt(rows);
}
}
The problem is caused by the forward loop. In this way, when you delete a row the index rows points no more to the next row but to the row after the next.
For example, you are on rows=10 and you need to delete it, after that, the rows is incremented in the loop to 11 but at this point the offset 11 of the Rows array is occupied by the row that was at offset 12 before the delete. Effectively you are skipping a row in your check after every RemoveAt.
The usual way to solve it is to loop backward (starting from the end and going toward the first row)
private void chkCPTData_Click(object sender, EventArgs e)
{
for (int rows = CPTData.Rows.Count - 1; rows >=0; rows--)
{
double SampleDepth =(double)System.Convert.ToSingle(CPTData.Rows[rows].Cells[0].Value);
if (SampleDepth > (double)System.Convert.ToSingle(analysisDepth.Text))
{
CPTData.Rows.RemoveAt(rows);
}
}
CPTData.Refresh();
}
Related
I'm working on a text-based Tetris game and running into some issues with clearing lines. I have my DeleteRow() method which deletes the given row by working upwards from the given row, overwriting each row's data with the data of the row above it. This seems to work:
/**
* Deletes row r from the array of landed Tetromino blocks.
* #param r The row to delete from the landed array.
**/
private void DeleteRow(int row) {
for(int r = row; r > 0; r--) {
for(int c = 0; c < Board.WIDTH; c++) {
// Overwrite the value of this column from the row above.
still[r, c] = still[(r - 1), c];
}
}
}
Where "still" is the 2D array defined as such.
private int[,] still;
And initialized here:
public Board() {
still = new int[Board.HEIGHT, Board.WIDTH];
}
But in my CheckRows() method, which is what calls DeleteRow(), I seem to be having an issue where it will clear the first row that's passed to it, but subsequent rows are either ignored or it will delete the wrong row:
/**
* Checks each row to see if they are full, and if so, deletes the row and adds to score.
* #param score A long representing the current score, passed as reference.
**/
public void CheckRows(ref long score) {
List<int> rowsToClear = new List<int>();
for(int r = 0; r < Board.HEIGHT; r++) {
bool zero = false;
for(int c = 0; c < Board.WIDTH; c++) {
if(still[r, c] == 0) {
zero = true;
break;
}
}
if(!zero) rowsToClear.Add(r);
}
// Delete all the rows that did not contain zeros.
if(rowsToClear.Count > 0) {
// Add to the score depending on the number of rows cleared.
score += (new int[4] { 40, 100, 300, 1200 })[rowsToClear.Count - 1];
// Delete each row in the list and then increment all other row indices to account for
// the fact that the rows above this one are being copied to this one.
for(int i = (rowsToClear.Count - 1); i >= 0; i--) {
DeleteRow(rowsToClear[i]);
rowsToClear.ForEach(x => x++);
}
}
}
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
I have noticed though that if those rows are not deleted in the first CheckRows() call, they will be in the next iteration of the main game loop.
Is there some flaw in the logic I'm using? This is my first time making a Tetris game. I'm doing this to familiarize myself with C#, and I'm just not sure what the issue is here. Can someone else spot what's wrong?
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
This is the line you are speaking of:
rowsToClear.ForEach(x => x++);
That line of code does absolutely nothing: The values will not be incremented.
You can perform an action on the element passed to the delegate in ForEach but you cannot replace it with a new value. Here is the documentation from MSDN:
Modifying the underlying collection in the body of the Action delegate is not supported and causes undefined behavior.
That will not work and neither will this:
foreach (var thisNum in nums)
{
thisNum++; // <== will not compile
}
This will increment the list:
for (int i = nums.Count - 1; i >= 0; i--)
{
nums[i]++;
}
<== Try Me ==>
After rearranging my CheckRows method around, I realized that the solution was that I needed to delete the rows in top to bottom order, as opposed to bottom to top.
I am using DataGridView in C# being civil engineer.
I want the values from a particular row. If the same value appears in a row, I want to display a message. For example, if 1 appears multiple time in a row, i want to display a message on every 1.
Here is how I am doing it:
private void button4_Click(object sender, EventArgs e)
{
for (int i = 0; i < dataGridView2.ColumnCount - 1; i++)
{
if (CategoryCLO[i] == 1)
{
MessageBox.Show("Here i am");
}
else
{
MessageBox.Show("I am not");
}
}
}
private void dataGridView2_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex == 1)
{
CategoryCLO.Add(int.Parse(
dataGridView2.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString()));
}
}
Problem:
It works right only one time. If a user replaces a value in a cell in DataGridView, it adds up another value corresponding to the newly added index of list.
Explanation:
The problem is that when user changes value in DataGridView, it got adds up in the list, thus increase the index of list , I want that index of list should be fix to the column index. and any change in particular cell , should change in that particular index rather than adding up.
Question:
I want that value should be placed in the previous index rather than creating new. In short, I want the indexes of list should be fixed to column number. How can I do it? any direction?
Note: Column numbers are variable depending upon the user
I have a DataTable with one column filled with list of words from a text file, I create a method to read a string if the string is founded the row must be deleted, but the problem is that the DataTable don't get the updates.
foreach(string line in file)
{
tagst.Rows.Add(line)
}
string s;
for (int k = 0; k < tagst.Rows.Count; k++)
{
s = tagst.Rows[k]["Tags"].ToString();
if(s.Equals("Jad"))
{
tagst.Rows[k].Delete();
}
}
after your loop, call tagst.AcceptChanges();
per the documentation:
When AcceptChanges is called, any DataRow object still in edit mode successfully ends its edits. The DataRowState also changes: all Added and Modified rows become Unchanged, and Deleted rows are removed.
As #LarsTech stated, you'll want to rework your loop like:
for (int i = tagst.Rows.Count - 1; i >= 0; i--)
{
// ....
}
One issue with your coding is that when you removing rows - you need to start from the last one backward for very simple reason. Once you remove row 3,10,20 out of 100 how many rows will remain? Why do you need to start from bottom? Because tagst will be automatically re-indexed and reordered as soon as delete completed.
But your tagst.Rows.Count is already set and not getting refreshed ever!
Basically once your counter(K) hit number where rows already been deleted you will see error at best, at worse your app will crush if you do not have error handling routines set. Since you did not post actual code for how you create your tgst I will show how it can be done in array. Declaration of variable ommited...
Try this:
for (int k=tagstArray.Count; k>0; k--)
{
s = tagstArray[k].ToString();
if(s.Contains("Jad"))
{
tagstArray[k].Remove(k);
}
}
I have two datagridviews in one form. The first, datagridview1 has columns and data:
name IC EMAIL TELEPHONE
------------------------------------------------------
rOO 898989096677 AB#YAHOO.COM 018-9097878
datagridview2 has
name IC EMAIL TELEPHONE ID
-----------------------------------------------------------
rOO 898989096677 AB#YAHOO.COM 018-9097878 8787
I would like to ask help on how to compare two datagridviews, as you can see in the figure I would like to compare the four columns from one datagridview to another datagridview and see if any results match. For example, does the roo name match with the another datagridview, I want the value in the id (8787) to be sent to another datagridview.
there is a very simple solution to compare twoo datagridview and show their result in 3rd one.
for (int i = 0; i < dtView1.Rows.Count; i++)
{
for (int j = 1; j < dtView1.Columns.Count; j++)
{
if ((dtView1.Rows[i][j]) == (dtView2.Rows[i][j]))
{
// here you can add your own logic
}
else
{
// here you can add your own logic
}
}
|
Let's assume that you populate both datagrids with the same objects that contain these 4 properties from your example. You must check for the selected row from the first datagrid if in the second datagrid there is an matching object, based on your filters. Use Linq. If so, then copy the data from the selected item from the first datagrid into the matching element from the seconds. I am affraid that you will need to do all these steps manually, because there is no method that can compare two data grids and you just set some filters and the job is done. You will have some work to do, but it is not hard. Good luck.
Steve's answer does not work correctly and will not compile.
Here is my solution:
int x = 0;
int y = 0;
int i = -1;
int z = 0;
foreach (DataGridViewRow row in dataGridView1.Rows)
{
i++;
if ((dataGridView1.Rows[i].Cells[i].Value) == (dataGridView2.Rows[z].Cells[i].Value))
{
x++;
}
else
{
y++;
}
if (z < dataGridView2.Rows.Count)
{
z++;
}
if(z == dataGridView2.Rows.Count)
{
z--; //subtract 1 from the total count because the datagrid is 0 index based.
}
MessageBox.Show("Matched: " + x.ToString() + "\r\n" + "Not Matched: " + y.ToString());
A foreach loop on the datagrid will cycle through each row, you can then select a cell or even cells to compare. This code is a bit lengthy and I'm sure it can be simplified, however it does the job.
Currently this will check every row from datagrid 2 for a match in datagrid 2, only on a single column.
Its import to remember the datagrid is 0 index based. The Count property of the datagrid gives a [1]index based count, so we need to subtract the last iteration on datagrid 2.
I have a virtual datagridview that I want to set varying row heights for. I was hoping to find a method for setting all the row heights at once, rather than looping through each one at a time.
This is the method that I tried to set the heights, but the performance is horrible ~1 second per 1,000 rows. For me, an average row count is ~20k-30k rows so this is unacceptable.
public void PopulateData()
{
this.SuspendLayout();
this.RowCount = Data.RowCount;
for (int i = 0; i < Data.RowCount; i++)
{
this.Rows[i].Height = Data.RowHeights[i];
}
this.ResumeLayout();
}
I made sure to turn off auto-sizing first also, but performance is still poor.
this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
this.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
Is there any way to pass in an array of row heights or prevent OnRowHeightChanged from being called when resizing rows?
Apparently if you create the rows independently of the datagridview, the performance-hindering features do not apply.
The trick is to create an array of rows, size them, and then add the range of rows to the datagridview afterwards:
public void PopulateData()
{
this.SuspendLayout();
DataGridViewRow[] rows = new DataGridViewRow[Data.RowCount];
for (int i = 0; i < rows.Length; i++)
{
DataGridViewRow row = new DataGridViewRow();
row.Height = Data.RowHeights[i];
rows[i] = row;
}
this.Rows.AddRange(rows);
this.ResumeLayout();
}
For 15,000 rows this only took 150 ms compared to 15 seconds without creating a seperate array, 100 times faster!
Try this instead of your code & see if you have any performance gains. Usually with virtual grids this works faster -
Add a handler for DataGridView.RowPrePaint:
dataGridView1.RowPrePaint += new DataGridViewRowPrePaintEventHandler(dataGridView1_RowPrePaint);
private void dataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
dataGridView1.AutoResizeRow(e.RowIndex);
}