Comparing two datagridviews - c#

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.

Related

Linking the Cells of a spreadsheet like excel to be able to perform formulas c#. Eg: A1 +C8

In Our assignment we were given a task to create a new spreadsheet program with a 26 x 26 grid of textboxes without importing anything and make use of array (in my case I Used array of objects).
I created a 2 d array of objects containing size of grid (27x27). the reason of it being 27 instead of 26 is because the first row and column is to show the ABCDE etc and 12345 etc of the rows and columns.
now the rows indexation I had no problem because it is numeric. however, the letters I solved by creating a string array with alphabet from a to z and entering them through a for loop and it worked.
Now the next step is to be able to link the cells, however, I am a bit stumped, because people told me I have to use Ascii or smthin.
Can anyone help me plz on how to achieve the cell links?
I have been able to past the name of each cell using this code but I fear I just filled the .Text of the cells not the cells name per se:
for (int x = 1; x < TwoDArrayCells.GetLength(0); x++) //nested loop to create the grid of textboxes.
{
for (int y = 1; y < TwoDArrayCells.GetLength(1); y++)
{
Cells CellFields = new Cells(Name = $"{AlphabetRow[x]}{y}", x, y + 3); // called a new cell named CellFields.
CellFields.Text = Name;//Change to location if you wise to take
this.Controls.Add(CellFields);
}
}
The result I would like is to be able to after this be able to link cells. meaning if I insert in a text box A1 + A2 it knows that a1 is the first box in the first row of first column and the a2 is the box in the second row of the first column.
Assuming that Columns can be A-Z only (Capitals), You can extract the column index and row index as follows:
Given a cell, string cell = "A2";
- the column index: int column = cell[0] - 'A' + 1;
- the row index: int row = Convert.ToInt32(cell.Substring(1));
Explanation: for the column, take the first letter, subtract it from 'A' (the subtraction will be performed on the assci codes of the characters) and add 1 since your actual cells are stored starting from index 1. for the row, just parse the rest of the string to int.

Creating a sudoku. Should I use a while statement for this code?

I'm making a sudoku in Windows Form Application.
I have 81 textboxes and I have named them all textBox1a, textBox1b... textBox2a, textBox2b...
I want to make it so that if any of the textboxes, in any of the rows, is equal to any other textbox in the same row, then both will get the background color red while the textboxes are equal.
I tried using this code just for test:
private void textBox1a_TextChanged_1(object sender, EventArgs e)
{
while (textBox1a.Text == textBox1b.Text)
{
textBox1a.BackColor = System.Drawing.Color.Red;
textBox1b.BackColor = System.Drawing.Color.Red;
}
It didn't work, and I don't know where I should put all this code, I know I shouldn't have it in the textboxes.
Should I use a code similar to this or is it totally wrong?
You want to iterate over the collection of text boxes just once, comparing it to those that haven't yet been compared against. If you have your textboxes in an array (let's call it textBoxes), and know which one was just changed (e.g. from the textChanged handler), you could do:
void highlightDuplicates(int i) // i is the index of the box that was changed
{
int iVal = textBoxes[i].Text;
for (int j = 0; j < 82; j++)
{
// don't compare to self
if (i == j) return;
if (textBoxes[j].Text == iVal)
{
textBoxes[i].BackgroundColor = System.Drawing.Color.Red;
textBoxes[j].BackgroundColor = System.Drawing.Color.Red;
}
}
}
If you wanted to get fancier, you could put your data in something like: Dictionary<int, TextBox>, where the key is the value and the TextBox is a reference to the text box with that value. Then you can quickly test for duplicate values with Dictionary.Contains() and color the matching text box by getting its value.
I think your current code would result in an infinite loop. The textboxes' values can't change while you are still in the event handler, so that loop would never exit.
If all of your boxes are named according to one convention, you could do something like this. More than one input can use the same handler, so you can just assign this handler to all the boxes.
The following code is not tested and may contain errors
private void textBox_TextChanged(object sender, EventArgs e){
var thisBox = sender as TextBox;
//given name like "textBox1a"
var boxNumber = thisBox.Name.SubString(7,1);
var boxLetter = thisBox.Name.SubString(8,1);
//numbers (horizontal?)
for(int i = 1; i<=9; i++){
if(i.ToString() == boxNumber)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + i + boxLetter) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
//letters (vertical?)
for(int i = 1; i<=9; i++){
var j = ConvertNumberToLetter(i); //up to you how to do this
if(j == boxLetter)
continue; //don't compare to self
var otherBox = Page.FindControl("textBox" + boxNumber + j) as TextBox;
if (otherBox.Text == thisBox.Text)
{
thisBox.BackColor = System.Drawing.Color.Red;
otherBox.BackColor = System.Drawing.Color.Red;
}
}
}
I believe you will be more effective if create an Array (or a List) of Integers and compare them in memory, against compare them in UI (User Interface).
For instance, you could:
1) Create an Array of 81 integers.
2) Everytime the user input a new number, you search for it in that Array. If found, set the textbox as RED, otherwise, add the new value to that array.
3) The ENTER event may be allocated fot the entire Textboxes (utilize the Handles keyword with all Textboxes; like handles Text1.enter, Text2.enter, Text3.enter ... and so forth)
Something like:
int[] NumbersByUser = new int[81];
Private Sub Textbox1.Enter(sender as object, e as EventArgs) handles Textbox1.Enter, Textbox2.Enter, Textbox3.enter ...
int UserEntry = Convert.ToInt32(Sender.text);
int ValorSelecionado = Array.Find(NumbersByUser, Z => (Z == UserEntry));
if (ValorSelecionado > 0) {
Sender.forecolor = Red;
}
else
{
NumbersByUser(Index) = UserEntry;
}
You should have a 2 dimensional array of numbers (could be one dimensional, but 2 makes more sense) let's assume its called Values. I suggest that you have each textbox have a incrementing number (starting top left, going right, then next row). Now you can do the following:
All TextBox Changed events can point to the same function. The function then takes the tag to figure out the position in the 2dim array. (X coordinate is TAG % 9 and Y coordinate is TAG / 9)
In the callback you can loop over the textboxes and colorize all boxes as you like. First do the "check row" loop (pseudo code)
var currentTextBox = ((TextBox)sender)
var x = ((int)currentTextBox.Tag) % 9
var y = ((int)currentTextBox.Tag) / 9
// First assign the current value to the backing store
Values[currentTextBox] = int.parse(currentTextBox.Text)
// assuming variable x holding the column and y holding the row of current box
// Array to hold the status of a number (is it already used?)
bool isUsed[9] = {false, false, ...}
for(int col = 0; col <= 9; i++)
{
// do not compare with self
if(col == x) continue;
isUsed[textBox] = true;
}
// now we have the status of all other boxes
if( isUsed[Values[x,y]] ) currentTextBox.Background = Red else currentTextBox.Background = Green
// now repeat the procedure for the column iterating the rows and for the blocks
I would suggest a dynamic approach to this. Consider each board item as a cell (this would be it's own class). The class would contain a numeric value and other properties that could be useful (i.e. a list of possible values).
You would then create 3 collections of the cells, these would be:
A collection of rows of 9 cells (for tracking each row)
A collection of columns of 9 cells (for tracking each column)
A collection of 3x3 cells
These collections would share references - each cell object would appear once in each collection. Each cell could also contain a reference to each of the 3 collections.
Now, when a cell value is changed, you can get references to each of the 3 collections and then apply a standard set of Sudoku logic against any of those collections.
You then have some display logic that can walk the boards of cells and output to the display (your View) your values.
Enjoy - this is a fun project.

How to read a column cell in Listview

I try to read whole rows of a given column in listview
As I'm new in C# and use of listview I saw it does not work as List
Below is the code that I use (What I found as solution in some sites)
for (int i = 0; i < rapview.Items.Count; ++i)
{
int idx = 0;
foreach (ColumnHeader header in rapview.Columns)
{
if (header.Text == "Bak. €")
{
MyArray[0] = GeneralMethod.GetClientName(conn, rapview.Items[1].Text);
di = new ListViewItem(MyArray);
tmpView.Items.Add(di);
}
++idx;
}
}
I want to retrieve 1st column 7th column cells, but I see that rapview.Items[1].Text is empty and similar for 7th cell.
Either I miss something or given solution is not correct.
How to read the value of those cells. If possible with column number by bypassing the foreach loop?
This is a loop that copies every value in the 7th column to a list of strings.
If there is no 7th column in the row it adds an empty string.
int column = 6;
List<string> values = new List<string>();
foreach (ListViewItem lvi in rapview.Items)
if (lvi.SubItems.Count - 1 < column) values.Add("");
else values.Add(lvi.SubItems[column].Text);
Note that C# counts zero-based, so the 7th column is indexed as 6.
You could test the value in row 1 column 7 as:
Console.WriteLine("Cell 7 in row 1 contains:" + values[0]);
Note that every Item in a ListView can have its own number of SubItems. Therefore we must test it before we access it!
Also note that SubItem[0] has the same value/text as the Item!

Weird issue when removing columns from a datatable

I want to remove every column after the 3rd column from a CSV file loaded into a datatable, but I'm getting odd results. Here's my code.
System.Data.DataTable csv_datatable = null;
using (System.IO.StreamReader re = new System.IO.StreamReader(model.file.InputStream))
{
csv_datatable = CsvParser.Parse(re as System.IO.TextReader);
for (int x = 3; x < csv_datatable.Columns.Count + 1; x++)
{
csv_datatable.Columns.RemoveAt(x);
}
}
My sample CSV file:
7 columns, and I want to keep the first three.
email,first,last,middle,nick,job,phone
roryok#fakedomain.com,rory,wally,ok,danger,developer,555-0123
This is the result I get.
email,first,last,nick,phone
roryok#fakedomain.com,rory,ok,danger,555-0123
As you can see, rather than removing the last columns as I would expect, the code actually removes the 4th and 6th columns.
As usual, figured this out as I was posting, but I'll post the solution anyway in case it helps someone.
As each column is removed, the index for each column changes. So when you remove column 3, 4 becomes the new 3. This code works:
for (int x = 0; x < csv_datatable.Columns.Count; x++)
{
csv_datatable.Columns.RemoveAt(3);
}
This will loop over the number of columns, and remove the third column over and over again until everything is gone.

C# iterate over a row in Excel

I need to iterate over a specific excel row. For now I've got a code to iterate over a column and I want it to be similar to that. It looks like this:
int columnLength = xlWorkSheet.UsedRange.Rows.Count;
string lastCell = Regex.Replace(CONST_FIRST_CELL, #"\d+", columnLength.ToString()); //will give me the last cell in the column
var excelColumn = xlWorkSheet.Range[CONST_FIRST_CELL, lastCell ];
if (excelColumn == null)
{
throw new Exception("bad col");
}
for (int i = 1; i < columnLength ; i++)
{
Excel.Range currentValue = excelColumn [i];
....DO SOME STUFF....
}
how can I iterate over a specific row? I'm not sure how to get the last column like I got the last row cell in the above implementation since then I just had to switch a number with the length but now I somehow need to get the correct last cell of a row (which means switching the letters somehow i.e C4 to AD4) in order to get the range of first cell row and last...
The best solution I guess involves a foreach loop somehow?
You were almost there, your loop just needs some tuning:
//Input all the Int values you want
int targetRow = 1;
int startCol = 1;
int maxCol = 10; //With this value the loop below will iterate until column 9 (inclusive)
for (int i = startCol; i < maxCol ; i++)
{
Excel.Range currentRange = (Excel.Range)xlWorkSheet.Cells[targetRow, i];
if (currentRange.Value2 != null)
{
string curVal = currentRange.Value2.ToString();
}
}
IMO this is the best way to iterate through cells (by considering rows and/or columns). You can do it differently (on the lines of what you were trying): iterating within the columns of a given range (you would need to define the range as Excel.Range Type and then rely on the in-built property Columns), although I think that this can be more confusing. Example: if you have as input range C1:H5, "C:C" is the first column, "D:D" the second column, etc. With my approach, the first column will always be "A:A", the second column "B:B", etc.
Example of iterating through columns in a given range (inputRange):
foreach(Excel.Range curCol in inputRange.Columns)
{
if (curCol.Value2 != null)
{
//As far as each column only has one row, each column can be associated with a cell
string curVal = curCol.Value2.ToString();
}
}

Categories