Datagridview.SelectedCells order - c#

I'm working on a C# application that contains a lot of DataGridViews which are empty. The user has to fill them with copy/pasted data from excel. What I do is the following:
int i = 0;
string s = Clipboard.GetText();
// Separate lines
string[] lines = Regex.Split(s, "\r\n");
foreach (string line in lines)
{
// Separate each cell
string[] cells = line.Split('\t');
foreach (string cell in cells)
{
// If we selected as many cells as copied
if (dataGridView.SelectedCells.Count == (lines.Length-1)*(cells.Length))
{
dataGridView.SelectedCells[i].Value = cell;
i++;
}
}
}
The problem is that if I copy something like this (on excel):
1 2 3
4 5 6
My datagridview will look like:
6 4 2
5 3 1
I don't really know what to do to fix this...
Thanks in advance

Convert your clipboard data to a 2-dimensional array. Remember length of each dimension.
Iterate through the selected cells and find the top-left and bottom-right cells. From that you can determine it is of the right size.
Using a double loop, map your clipboard data from array position directly to cell coordinate (not using selected cells) using the topleft cell coordinate as an offset.
Alternatively, rather than a 2 dimensional array, you could create a List of a small class/struct containing the properties Row, Col and Value. Then just iterate through that rather than the double loop.

Replace
dataGridView.SelectedCells[i].Value = cell;
with
dataGridView.SelectedCells[(dataGridView.SelectedCells.Count-1) - i].Value = cell;

Related

How to merge same values together using C# and ClosedXML?

I'm trying to merge the same values together for Excel using ClosedXML package in C#.
This is my current output.. it's not merging the same values together and leaving some out.
e.g. No.7 merged two cells and not the third.
e.g. No.9 keeps merging 2 cells and repeats the same merge.
My current logic:
// Foreach record
int i = 1;
foreach (var record in ...)
{
i++;
// Minimum Months
// Create Value
worksheet.Cell($"D{i}").Value = record.MinimumMonths.ToString();
// Evaluate value - if they're the same then merge
if(i > 2 && worksheet.Cell($"D{i}").Value.ToString() == worksheet.Cell($"D{i - 1}").Value.ToString())
{
worksheet.Range($"D{i - 1}:D{i}").Merge();
}
}
I don't understand where my logic is going wrong. How can I merge all the same values together?
worksheet.Range($"D{i - 1}:D{i}").Merge();
You shouldn't be merging your cells in pairs. Identify the first and last cells of a range of cells with the same value and merge that entire range in one .Merge() statement.
Also notice that when you merge a range of cells, only the top left cell keeps its value. All the other cells' values in the merged range are cleared. This is in line with what Excel does.
Your .Value.ToString() equality comparison is a bit naive. Rather check with cell1.Value is double d1 && cell2.Value is double d2 && d1.Equals(d2).

Slow writes to Excel cell using Excel Interop in Winforms application

I'm looking some help with my code for writing data to an Excel worksheet.
I'm currently writing data to a single cell at a time which is taking around three seconds per cell. As you can imagine, this time really starts to add up when you start to get into hundreds of cells.
I'm aware that there is a method of saving to a range of cells but not entirely sure how to do this.
The document I am writing to is a questionnaire style spread sheet with columns: Number, Question, Yes, No, NA.
I loop through a list of user controls which hold the users selected answer from my form and then write it to the cell.
Any help appreciated, thanks.
if (QuestionList.Count > 0)
{
//loop round rows
for (int i = 0; i < QuestionList.Count; i++)
{
//check that index is not null
if (!String.IsNullOrEmpty(QuestionList[i].Index))
{
//if the char in index is a letter
if (Char.IsLetter(QuestionList[i].Index[0]))
{
worksheet2.Cells[i + 1, 4].Value2 = QuestionList[i].Yes;
worksheet2.Cells[i + 1, 5].Value2 = QuestionList[i].No;
worksheet2.Cells[i + 1, 6].Value2 = QuestionList[i].NA;
}
}
}
}
The simple way to read multiple cell values in one go is to create a new Excel range, specifying the range of cells that you want to read.
You can then take the values within the range and put them into a multidimensional object array.
Then simply refer back to the cell position within the array to get values.
This will do one single read to get multiple cell values rather than a single read for each cell.
string name;
string dob;
string address;
//excel range
Excel.Range range = (Excel.Range)xlWorksheet1.Range["A1", "I10"];
//range array
object[,] rangeValueArray = range.Value2;
//cell 1A
if (rangeValueArray[1,1] != null)
{
name = rangeValueArray[1, 1].ToString();
}
//cell 1B
if (rangeValueArray[1, 2] != null)
{
dob = rangeValueArray[1, 2].ToString();
}
//cell 1C
if (rangeValueArray[1, 3] != null)
{
address = rangeValueArray[1, 3].ToString();
}

Reading data from datagridview and saving into list

I have a C# program that stores some data in a datagridview called DGV.
DGV has 6 columns and has the following structure:
Column1: SALES_US
Column2: QUANTITY_US
Column3: SALES_EU
Column4: QUANTITY_EU
Column5: SALES_AS
Column6: QUANTITY_AS
The programs fills the columns labelled as "SALES" (e.g. columns 1-3-5) with the name of some items. Then in the columns labelled as "QUANTITY" (e.g. columns 2-4-6) the user input some numbers only if some of the item has been sold.
E.g. if in row 1 I have the item A, in cell[1, 0] (so row 0, column 1, supposing that rows/columns are indexed starting from 0) I can put 1000 if 1000 items A have been sold in US, and so on.
So, after user input, the DGV will have in column 1-3-5 (again, supposing that index starts at 0) some numbers but it is also possible that some cells are empty.
So I need to loop through the columns where the user has input the quantities and store the quantities in a list (in my example below I have created a list of string, called "myKeys").
Is there a quick/efficient way of doing this? Please consider that I am not super expert and so far I have only found the following (which works but seems very slow).
class organizer
{
public List<string> organize(DataGridView DGV, DataGridView DGV2,
DataGridView DGV3)
{
List<string> myKeys = new List<string>(); //This is the list of quantities
// the cells of DGV are accessible using [col, row] coordinates
for (int counterRows = 0; counterRows < countRows; counterRows++)
{
try // look for data in the second column
{
test = DGV[1, counterRows].Value.ToString(); // if the cells is empty the string conversion is not possible and no info will be stored
myKeys.Add(DGV[1, counterRows].Value.ToString());
}
catch{}
try // look for data in the fourth column
{
test = DGV[3, counterRows].Value.ToString(); // if the cells is empty the string conversion is not possible and no info will be stored
myKeys.Add(DGV[3, counterRows].Value.ToString());
}
catch{}
try // look for data in the sixth column
{
test = DGV[5, counterRows].Value.ToString(); // if the cells is empty the string conversion is not possible and no info will be stored
myKeys.Add(DGV[5, counterRows].Value.ToString());
}
catch{}
}
// here the same for-loop is repeated to add the quantities to the list reading from the other datagridviews.
return myKeys;
}
}
In this case I loop through the cells with a for loop and use the try-catch to see if the value can be converted to string. If the cell is empty, the ".ToString()" will fail and the following line that add the cell value in the list "myKeys" is not executed.
But I need to specify directly that I first work on column 1, the 3 and finally 5.
Apart from the (probably) useless code duplication, the code seems to run quite slow for such a small datagridview (I have less than 50 rows). I need to do this on other datagridviews, applying same logic.
Your suggestion about better way to deal with the problem are very appreciated.

dataGridView form component and colored m*n table

I'm trying to display the table of m*n cells with some text in each cell, and the background colors of each cell could be different.
Am I right the dataGridView component could be used exactly for this purpose?
If yes, then how to make the dataGridView to contain more then just one empty row? Let's say I want it 5*5 cells and the cells could be empty.
You can add values to a DataGridView control in many ways: from a database, from a Collection (Array, DataTable, etc.), directly row by row, etc. In each cell you can put the (string) values you want, including ""/empty. Here you have a sample code to get some inspiration:
int count = 0;
int maxCount = 5;
do
{
count = count + 1;
//dataGridView1.Rows.Add("col1", "col2", "col3", "col4", "col5");
dataGridView1.Rows.Add(); //For adding empty rows, you can use this one
} while(count < maxCount);
dataGridView1[1, 2].Style.BackColor = Color.Yellow;
dataGridView1[3, 1].Style.BackColor = Color.Yellow;
dataGridView1[4, 4].Style.BackColor = Color.Yellow;
It takes dataGridView1 (a DataGridView with 5 columns added via "Design View"), adds 5 rows to it and colors the background of various cells.

How to read the data from Non Adjacent Cells in Excel using C#

I have an excel sheet, in which i have multiple cells selected, which are not adjacent to each other. When the user click on the button, we need to read all the cells data, process it and write it to some other cell.
If the cells are adjacent to each other, i was able to get the range and able to perform the operation. But if the cells are not adjacent to each other, i am not able to get the range.
The Selection.Range is always giving the address of the last cell we selected.
But we need to get the addresses of all Cells, which i am not able to do it.
Please can anybody suggest me a way to handle this scenario.
Sample code:
Range objRange = (Range) Globals.ThisAddIn.Application.Selection;
int nColCount = objRange.Columns.Count;
int nRowCount = objRange.Rows.Count;
Vinay,
I have tried this code based on your suggestion,
Range objRange = (Range) Globals.ThisAddIn.Application.Selection;
foreach (Range cell in objRange)
{
MessageBox.Show("" + cell.Value2);
}
But it didn't worked. Always it's giving the last selected cell. i have selected A1, A4, A13, A16 cells. But this code is returning the A16 Cell Value Only.
Range inherits from IEnumerable. So you can use for each iterator to enumerate via all cells.
See the equivalent VBA code below:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim result As String
result = ""
Dim c As Range
For Each c In Me.Application.Selection
result = result & ", " & c.Text
Next c
Me.Cells.Item(1, 1).Value = result
End Sub
You can always use Range.Row, Range.Column for getting the cell address.
As said in comments, use foreach synatx:
foreach(Range cell in objRange)
{
// now access cell's properties - c.Value will give value
}
After trying alot, I got the answer.
Here is the working code,
Areas objAreas = (Areas)objRange.Areas;
foreach (Range area in objAreas)
{
string CellAddress = (GetExcelColumnName(area.Column) + "" + area.Row);
MessageBox.Show(CellAddress);
}
GetExcelColumnName is the custom function u have written to convert Column number to Column Code(like a, b,... aa, ab.. etc)

Categories

Resources