I'm creating a program for test management that has a stats function, which calculates the number of bugs that have been fixed, not fixed or N/A.
The test cases are all listed on a DataGridView, where the 1st column is for the test cases, the 2nd one is for the results (the column I'd like to work with) and the latter is just for comments.
Here's a bit of my code to show what I'm talking about
private int Passed() // This method is supposed to count how many test cases have passed
{
int passed = 0;
if (/*what condition should I put here?*/) {
passed++;
}
return passed;
}
//Is this the best way to display the percentage in real time?
private void Refresh_Tick(object sender, EventArgs e)
{
Display2.Text = Passed().ToString();
}
The "Results" column cells have each a combobox with its items being "FIXED", "N/A" and "NOT FIXED".
Please, I'd like to know how I can programmatically access those cells' value and then use them as a condition to count how many bugs have been fixed.
Iterating through all the rows in the gridview should get you the answer.
int countFixed=0;
int countUnFixed=0;
for(int i=0;i<dgv.RowCount;i++)
{
if((string)dgv.Rows[i].Cells[1].Value == "Fixed") //try referring to cells by column names and not the index
countFixed++;
else if((string)dgv.Rows[i].Cells[1].Value == "Not Fixed")
countUnFixed++;
}
Related
So, I'm writing a program that is reads table data and puts cells values in a List. I made it, but there is one problem – UsedRange takes all cells on sheet so there is more items then I need and also, when I specify range by ["A:A", Type.Missng] it gives me an exception:
System.ArgumentException: "HRESULT: 0x80070057 (E_INVALIDARG))"
So my question is how to make it correctly?
Code is:
foreach (Excel.Range row in usedRange)
{
for(int i=0; i<lastCell.Row; i++)
{
if (row.Cells[4, i + 1].Value2 != null)
{
personlist.Add(Convert.ToString(row.Cells[4, i + 1].Value2));
}
else { i++; }
}
foreach(var person in personlist) {
Console.WriteLine(person);
}
}
UPD: I need a last used row, that's why I'm using UsedRange. So if there is any alternatives, like, checking if(!=null)? I will gladly try it
Tried to give it specific range, some tries to made a code like here C# - How do I iterate all the rows in Excel._Worksheet?
and here
https://overcoder.net/q/236542/программно-получить-последнюю-заполненную-строку-excel-с-помощью-c
but maybe I'm a dumb one, 'cause there is literally more than one articles about it and non of it works with me
The problem is 'used range' can include empty range (who knows how excel decides that magic number - if you type a letter on some arbitrary row and then delete it Excel can decide that cell is still part of your used range). You want your own custom definition of what a 'usedRange' is, which presumably is the range of non-blank rows. There's two straightforward ways of implementing this yourself (which gives you added control over it should you want to customize it).
You can just filter the list after the fact removing all blank entries. Or you can process the list in reverse, skipping rows till you find one matching your criteria
bool startProcessing = false;
for(int i=lastCell.Row-1; i>=0; i--)
{
if(!startProcessing){//bool is in case you want blank rows in the middle of the file, otherwise check valid row always
//check if valid row
//continue; if not, set startProcessing to true if yes
}
if (row.Cells[4, i + 1].Value2 != null)
{
personlist.Add(Convert.ToString(row.Cells[4, i + 1].Value2));
}
//else { i++; } //this is a bug, will cause a line skip
}
Also, as an aside - when you call i++; in the body of your for loop, it then calls it again in the header of your for loop and i += 2 skipping a row. Use continue; or just remove the else block altogether.
There's probably a way to get a cellRange matching your criteria, but imo doing it yourself can be better - you can ensure it does exactly what you want.
In my dataGridView cells values are 0's and 1's. I have to set 0 to A and 1 to P by using following code for this operation.
foreach (DataGridViewRow row in dataGridView1.Rows)
{
int d = Convert.ToInt32(row.Cells[0].Value);
if (d == 0)
row.Cells[0].Value = "A";
else
row.Cells[0].Value = "P";
}
but it gives error, error is like
System.Exception: A is not a valid value for Int32. -> System.FormatException: Input string was not in a correct
format.
at System.Number.StringToNumber(String str, NumberStyles options NumberBuffer& number, NumberFormatinfo info, Boolean parseDecimal) at System.Number.Parselnt32(String s, NumberStyles style, NumberFormatinfo info) ... continue
If I set the numerical value in above program it update successfully but if I set string value like "A" or "P" it gives above error.
Please give me the solutions.
In the future, to avoid this unnecessary back and forth inquisition, it would behoove you to include all the pertinent information in your question. Example in this case, the code that adds the data to the grid would benefit YOU by removing any question as to “how” the code is doing this. If others wanted to help you, then they are going to have to “guess” how this is done. If others (who you are asking help from) have to do a bunch of guessing and code writing to reproduce YOUR question… then most are going to move on.
I highly recommend that you take a look at the SO section on… How to create a Minimal, Reproducible Example ...I can not stress how helpful this “process” can be for YOU. Often times, the process of creating the “reproducible” example that you want to post here “reveals” the problem you are having. Posting a “complete reproducible example` will help you since the other people helping you don’t have to guess or write a bunch of code. Without question, you will get more and better answers to your questions if you do this simple thing.
As seen from the back and forth questions and answers, it seems to me that we are getting nowhere. Given your last comment, I am guessing you may be looking at this from the point of view of the DataGridView your comment…
”I have recently changed the column type in string by using code "dataGridView1.Columns[ 1 ].ValueType = typeof(string);". I have also
confirm to print the column type by using code
"Console.WriteLine(dataGridView1.Columns[1 ].ValueType);" before and
after execution of the code "dataGridView1.Columns[1 ].ValueType =
typeof(string);"”
Changing the “ValueType” IN THE GRID is fine, however, you need to keep in mind, that the “underlying” data source won’t necessarily respect/conform to the “GRIDS” value type. In fact in most cases, as this one… it WON’T. You can test this by doing the following steps:
1) Load the data into the grid such that the column we want to change is a “numeric” column.
2) Change that column to a string value type as your posted code does in your comment.
3) Type an “A” into the column you just changed to a string type then press the “Enter” key…
I am betting you will get a DataError exception.
It is true, your code changes the column type to a string IN THE GRID, but the underlying data source is still an int. Hence the DataError. The DataGridView simply displays the data that is in the data source. You can change the column types, format and order in the GRID, however, the underlying data source “usually” will NOT change. This is why, in your case, you need to “change” the column type in the data source, not the grid.
There are numerous ways to achieve this, however, IMHO I am betting the “easiest” way to do this would be to change the column type and possibly the values also “when you get the data from the data base.” I am aware this may not be an option at times and you are forced to make the changes you are asking for. But if you can do this directly from the data base, I recommend it.
Therefore, lets break down what I can gather you are asking. For starters, I assume you are getting the data from a data base and the data is returned in a DataTable. One of the columns in this DataTable is of some “numeric type” (‘intorDouble`) and it ONLY contains values of zeros (0s) and ones (1s). Is what you want, is to “change” the values in that column to “As” and “Ps” such that all zeros (0s) become “As” and ones (1s) become “Ps.” I hope I have this correct.
This appears straight forward, and I assume there are numerous ways to do this, however, I am confident you can NOT simply “change” a columns data “type” in an existing DataTable. Therefore, it seems obvious that your code is going to have to “ADD” this column to the existing DataTable. Something like…
originalDataTable.Columns.Add("A/P", typeof(string));
Then “after” the column as been added, loop through all the rows in the DataTable and set the values for the new “A/P” column. I am aware that this creates an extra column, however, it would be a simple task to remove or simply not display the column of 0s and 1s. Given this, a method Add_A_P_TextColumn(DataTable dt, string colName) … that takes a DataTable we want to add the column to and a string name to identify the column that contains the 0s, and 1s… may come in handy and look something like…
private void Add_A_P_TextColumn(DataTable dt, string colName) {
dt.Columns.Add("A/P", typeof(string));
foreach (DataRow row in dt.Rows) {
row["A/P"] = (int)row[colName] == 0 ? "A" : "P";
}
}
This should add the column as described and it is your choice to either remove the column of 0s and 1s or not. To demonstrate a complete and reproducible example, the complete code below demonstrates using the above method. To start, drop a DataGridView and a Button onto a form like below.
When the form loads, the code gets a column of random 0s and 1s for test data. This would be the original data your code gets from the data base. This is what the picture shows. When the user clicks the button, the Add_A_P_TextColumn method is called to add the "A/P" column.
DataTable OriginalTable;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
OriginalTable = GetOriginalTable();
dataGridView1.DataSource = OriginalTable;
}
private DataTable GetOriginalTable() {
DataTable dt = new DataTable();
dt.Columns.Add("0/1", typeof(int));
Random rand = new Random();
for (int i = 0; i < 15; i++) {
dt.Rows.Add(rand.Next(2));
}
return dt;
}
private void button1_Click(object sender, EventArgs e) {
Add_A_P_TextColumn(OriginalTable, "0/1");
}
I hope this clears some things up.
you need to iterate over Cells as well:
foreach (DataGridViewRow row in dataGridView1.Rows)
{
foreach(DataGridViewCell cell in row.Cells)
{
int d = Convert.ToInt32(cell.Value);
if (d == 0)
cell.Value = "A";
else
cell.Value = "P";
}
}
I've tried searching the forums to find the answer, but they all seem to check for an integer value, not a string.
I've got a C# form pulling data values from an SQL table and displaying them on a datagridview. The column I wish check the string value for is labeled "Type", and is the third column in my data table. I wish to highlight all rows red that contain the word "pharmacy" in the third column.
private void invTableDataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in invTableDataGridView.Rows)
{
if (row.Cells[2].Value.ToString() == "Pharmacy")
{
invTableDataGridView.DefaultCellStyle.BackColor = Color.Red;
}
}
}
When I run the program with this code, nothing changes, even when I conduct actions that should trigger the databindingcomplete event. Frustratingly, if I change row.Cells[2] to row.Cells[3] where I have integer values, and type
if (row.Cells[3].Value.ToString() == "0")
the code works, but for the wrong column. It only seems to work with integers, but I want it to work with a string. How do I do this?
Edit: Yes, sorry, I should have been consistent. The value I wish to compare to is "Pharmacy", capitalized.
Edit2: Ok I found out why it was giving me so much trouble. The values in column[2] were being added in by a combobox, and for some reason it always adds two spaces after the word even though it does not show the spaces in the string collection editor.
Because your code works with column of integers, then problem must be in the string comparing.
As I mentioned in the comments check what data you have and value comparing to.
Or you can execute ToLower() on the cell value and compare it to "pharmacy"
And you can use CellFormatting event handler. This event should be good for formatting control based on the values
private void invTableDataGridView_CellFormatting(Object sender,
DataGridViewCellFormattingEventArgs e)
{
Int32 yourindexColumn = 2;
if (e.RowIndex < 0 || e.ColumnIndex <> yourindexColumn)
Return;
//Compare value and change color
DataGridViewCell cell = invTableDataGridView.Rows[e.RowIndex].Cells[yourindexColumn]
String value = cell.Value == null ? string.Empty : cell.Value.ToString()
if (value.ToLower().Equals("pharmacy") == true)
{
invTableDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Red;
}
}
Simply i have a textbox that my users will enter in a Fan rpm.
I need my program to take the entered fanrpm and compare it with a database item called classrpm. My query from the database Will only return 1 item ever.
I need fanrpm to be > classrpm, and if fanrpm >= class rpm a msg box to pop-up and state "Fan RPM exceeds ClassRPM(which is x), please enter a value under ClassRPM.
The code below was my test with fake variable names. I attempted to take and convert the user entered text and the database retrieved item, then using the converted values to do the above if statement, and then output a msg box to inform me if it succeeded.
private void textBox1_TextChanged(object sender, EventArgs e)
{
txfanrpm.MaxLength = 4
string classrpm;
string fanrpm;
using (Fanrpm ds = new Fanrpm(cbdesigntype.SelectedValue.ToString(), cbfansize.SelectedValue.ToString(), cbfanclass.SelectedValue.ToString()))
{
DataTable dt = ds.dataset.Tables[0];
List<string> coolList = new List<string>();
foreach (DataRow row in dt.Rows)
{
coolList.Add(row[0].ToString());
}
classrpm = coolList.ToString();
fanrpm = txfanrpm.Text.ToString();
int classrpmInt;
int fanrpmInt;
classrpmInt = Convert.ToInt32(classrpm);
fanrpmInt = Convert.ToInt32(fanrpm);
if (fanrpmInt >= classrpmInt)
{
MessageBox.Show(this, "user entered fanrpm higher then class rpm which is yadda yadda");
}
else if (fanrpmInt < classrpmInt)
{
MessageBox.Show(this, "Fanrpm is less then Classrpm");
}
}
}
I believe my problem stems in converting my database item into something usable to compare too. Also i think that if my setup works as intended it may tell a user entering 500 at each entry the textbox will refresh and redo the query leading to potential wasted processing power?
-Edit my sql statement works, Though putting the data into a list may be one of my problems.
No way that you could convert a List(Of String) to a single string in that way.
And what if your user types not a number in your textbox?
However, if, as you have said, your DataTable contains always only one row then you could simplify your code to something like this
private void textBox1_TextChanged(object sender, EventArgs e)
{
int classRPM;
int fanRPM;
using (Fanrpm ds = new Fanrpm(....)
{
DataTable dt = ds.dataset.Tables[0];
// Get and convert the value in the field named ClassRPM (assuming is a string)
classRPM = Convert.ToInt32(dt.Rows[0].Field<string>("ClassRPM"));
// Now check if the textbox contains a valid number
if(!Int32.TryParse(txfanrpm.Text, out fanRPM))
{
MessageBox.Show("Not a number....");
return;
}
// Start your logic
if (fanRPM >= classRPM)
{
MessageBox.Show(.....);
}
else if (fanRPM < classRPM)
{
MessageBox.Show(.......);
}
}
}
I think that a TextChanged event is not the proper place for this kind of code. This will trigger everytime your user presses a key and result in a trip to the database for every key press. So if your user wants to type 500 you end asking the db first for "5", then for "50" and finally for "500".
I would prefer to let the user types freely its values and then check when a verify button is pressed or, if this is not possible, to check in the Control.Validating event.
Calling List.ToString() just returns "System.Collections.Generic.List`1[System.String]" rather than any items in the string. (The "ToString()" method is actually inherited from Object, and just returns a string that represents what type of object the object is, except in special cases like StringBuilder.)
I would avoid the TextChanged event because that event will cause your code to execute ever keystroke, which might cause errors when a value is partially entered, or when a user accidentally enters and invalid value and has to backspace.
i have one datagridview.in my datagridview is having four row and three columns.after enter the one row. that row one coloumns value repeat (or) same in second row columns value.so,how to find out the which value is repeat and which row.
If your grid data is mainly text then maintain a List<string> that holds all of the values being inserted in the DataGridView.
So every time data was entered in a cell (there are events you can handle for that, like CellEndEdit or CellLeave), you check if it is in your list of strings before you add it.
If it is, then you found a value already contained within the grid.
EDIT
Here is some sample code:
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
string cellVal = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
if (String.IsNullOrEmpty(listGridVals.Find(delegate(string s) { return s == cellVal; })))
listGridVals.Add(cellVal);
else
MessageBox.Show("Value: " + cellVal + " already in the grid!");
}
NOTE
This routine is very basic, it only checks if values being entered in the grid are
already in the List<string>.
However, let's say that you already have value test in one cell and already in the List<string>.
What happens when you delete that value and enter a test1 ? Well, test will still be in List<string> and next time you enter test in another cell you'll get the MessageBox saying there is already a test in the grid, which could be wrong.