Winforms reading data from excel table with specific columns - c#

I'm trying to read data from an excel table which this piece of code performs perfectly
However, this will read all columns from the table which I don't need, I only need specific columns from the excel table
int fabricHeaderRow = findFabricHeader(excelRange); //row number 26
int rows = excelRange.Rows.Count;
int cols = excelRange.Columns.Count;
myNewRow3 = null;
for (int i = fabricHeaderRow + 2; i <= rows; i++)
{
myNewRow3 = bomTable.NewRow();
if (excelRange.Cells[i, 1].Value2 != null) //Checks that the item column isn't empty
{
for (int j = 1; j <= cols; j++)
{
if (excelRange.Cells[i, j].Value2 == null)
{
myNewRow3[j - 1] = string.Empty; //inserts empty string into datagrid row[i] if the cell is empty
}
else
{
myNewRow3[j - 1] = excelRange.Cells[i, j].Value2.ToString();
}
}
if (myNewRow3 != null)
{
bomTable.Rows.Add(myNewRow3); //adds a new row to datable if not null
}
}
else
{
break; //break out of the outer for-loop as it reached an empty excel row
}
}
Excel table example
For example, from the table, I only need ID, COLOR, DESCRIPTION, SUPPILER and COST columns in my datarow, I'm stuck at trying to figure out how to read specific columns with multiple rows into a datarow.
public string[] bomArr = { "ID", "COLOR", "DESCRIPTION", "SUPPLIER", "COST"};
//for loop here
if (bomArr.Any(x => excelRange.Cells[27, j].Value2.Equals(x))){
myNewRow[currentCount] = excelRange.Cells[i, j].Value2.ToString(); //string
currentCount++;
}
I'm thinking of trying this, basically it'll only add to the datarow if any values in the array matches with a column header. But I only can make it work for tables with a single row, multiple rows make it more complicated.

Related

is there a way to select row or column table in Word document VSTO C#?

I have a table in Word document. I want to able to select a row or column or only a cell or whole table to modify.
now I can select the complete table and modify all cells, but I can not select a single row or column or cell.
I can not create a range for this purpose.
below my code for select whole of table:
if (WordAddIn.Config.ApplicationConfig.TemplateLanguage.ValueToShare == true)
{
for (int i = 1; i <= WordManager.Selection.Tables[1].Rows.Count; i++)
{
for (int j = 1; j <= WordManager.Selection.Tables[1].Columns.Count; j++)
{
...
}
}
else
{ }
}
}
Thoughts?
Create a macro of what you need. Look at the VB calls of the macro and mimic/use those calls in your code.
You have to know the index of your table/row/column, but this is how you get the reference to each. From there, you can edit, delete, add, etc.
Word.Table myTable = Globals.ThisDocument.Tables[0];
//Get Row
Word.Row myRow = myTable.Rows[0];
//Get Column
Word.Column myCOl = myTable.Columns[0];
//Get Cell
int rowNum = 0;
int colNum = 0;
Word.Cell myCell = myTable.Cell(rowNum, colNum);

How to edit data table column header names using c#?

I have some logical block in my C# program, as I am new to C# programming.
I have a data-table with duplicate header column names. I have to change my duplicate header like concatenate the name of the prior column to make these headers unique. Table names are coming dynamically.
Current datatable dTable
ID |Name|Age| School | Name | state| Part| Country|Division|Part
Expected dTable
ID |Name|Age| School | Name+school | state| Part| Country|Division|Part+Division
What I have tried and blocked here below
public DataTable RemoveDuplicateRows(DataTable dTable)
{
string[] columnNames = dTable.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
for (int i = 0; i < columnNames.Length; i++)
{
columnNames[i] = columnNames[i].Split('.')[0].Trim();
}
for (int i = 0; i < columnNames.Length; i++)
{
// create nested loop for compare current values with actual value of arr
for (int j = i + 1; j < columnNames.Length; j++)
{
if (columnNames[i] == columnNames[j])
{
var previous = columnNames[i - 1];
var current = columnNames[i];
columnNames[i] = current + previous;
// blocked here
// only one header is concatenating
// how can I add this newly edited columns to my datatable
}
}
}
return dTable; //cant get updated column headers
}

How to combine multiple arrays (that represent columns of data) into a nested/jagged array (or even a 2D) array

I have a program that I'm writing to extract certain data from various excel spreadsheet.
The process so far is:
for each spreadsheet identified:
-read in the data as a multidimensional array using interop excel (Even though it is slow, it is the best choice due to all of the different file formats I need to read in)
Sample: object[,] cellValues = (object[,])range.Value2;
-Identify the columns that I actually need and what order I need them in. This is stored in a jagged array of bytes:
byte[][] targetColumns
-THe jagged array essentially is (columnIndexFromSpreadsheet, preferredColumnOrder) e.g. if the first column in the spreadsheet should be read in as column 10 it would be (1, 10)
-I sort the jagged array by the preferred column order (that way I can just loop through the array in that order and extract those columns):
public static byte[][] SortTargetColumns(byte[][] targetColumns)
{
return targetColumns.OrderBy(x => x.Skip(1).First()).ToArray();
}
-I then extract that column by creating an array from that column index of the multidimensional array. This is the method that is called:
public static object[] ExtractColumn(object[,] dataArray ,byte columnIndex)
{
return Enumerable.Range(ArrayIndexStart, dataArray.GetLength(0)).Select(x => dataArray[x, columnIndex]).ToArray();
}
Usage:
array = ExtractColumn(dataArray, (byte) colIndex);
Now I am trying to piece these extracted arrays back together to make it readable. I will need to do some manipulation on some of the columns and then write to a text file after consolidating. The only problem is that I have no idea how to do this correctly. I have tried the following methods but continue to get a null reference exception:
// Get Row Count of dataArray
int rowCount = dataArray.GetLength(0);
// Create List to store extracted arrays
List<object[]> extractedDataList = new List<object[]>();
// Loop through target columns and extract the column as an array
for (byte colIndex = 1; colIndex <= targetColumns.Length + 1; colIndex++)
{
object[] array = ExtractColumn(dataArray, (byte) colIndex);
extractedDataList.Add(array);
}
// Create jagged array
object[][] extractedDataArray = new object[rowCount][] ;
for(int i = 0; i < extractedDataArray.GetLength(0); i++)
{
List<object> row = new List<object>();
for (int j = 0; j < extractedDataList.Count; j++)
{
row.Add(extractedDataList[j][i].ToString());
//extractedDataArray[i][j] = extractedDataList[j][i].ToString(); <-- null reference
}
extractedDataArray[i] = row.ToArray();
}
I'm at a loss of what else to try to put these column arrays back in a form that I can easily work with. Any and all tips/recommendations would be greatly appreciated.
Whenever you get confused like this, break the problem down into small pieces, and use meaningful names.
Let's say you have an array of columns, each of which has one element per row. That might be declared like this:
object[][] columns;
First, let's get the row and column counts:
var columnCount = columns.Length;
var rowCount = columns[0].Length;
Now write a small local function to accept a row and column index and return the right cell. In case not all of your columns have the same number of rows, you can include a boundary check and just return null if a cell isn't there.
object Getter(int row, int col)
{
bool outOfBounds = (row >= columns[col].Length);
return outOfBounds ? null : columns[col][row];
}
Now all we have to do is iterate over the rows to create the inner arrays:
object[][] target = new object[rowCount][]
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
}
And add in the code that uses the getter to populate the cells:
object[][] target = new object[rowCount][];
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
for (int col = 0; col < columnCount; col++)
{
var cellValue = Getter(row, col);
target[row][columnCount] = cellValue;
}
}
All together, it is simple to read:
var columnCount = columns.Length;
var rowCount = columns[0].Length;
object Getter(int row, int col)
{
bool outOfBounds = (row >= columns[col].Length);
return outOfBounds ? null : columns[col][row];
}
object[][] target = new object[rowCount][];
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
for (int col = 0; col < columnCount; col++)
{
var cellValue = Getter(row, col);
target[row][columnCount] = cellValue;
}
}

Winforms: Error in iterating excel rows of same column

Apologies for somewhat confusing explanation. I am using Microsoft.Office.Interop.Excel. Purpose for code below is to
1) Iterate all rows in Column B of excel sheet named oSheet1
2) Iterate all rows in Column 0 of datagridview (DTG)
3) If data from Column B and Column 0 matches, then export data in Column 1 of DTG into Column C of excel.
Hence, the data in column 1 of DTG is in reference with data in Column 0 of DTG. And data in Column C of excel will eventually be in reference with Column B of excel. I've inserted some images for easy understanding
I've tried multiple codes for hours and kept getting error. Below are my codes along with errors experienced:
Error: Cannot perform in runtime binding on null reference
for (int i = 1; i <= oSheet1.Columns.Count; i++)
{
string cellvalue = oSheet1.Cells[i, 2].Value.ToString(); //error here
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if ((string)row.Cells[0].Value == cellvalue)
{
for (int j = 0; j < dataGridView1.Rows.Count; j++)
{
oSheet1.Cells[i, 3] = dataGridView1.Rows[j].Cells[1].Value.ToString();
}
}
}
Error: Exception from H result
for (int i = 1; i <= oSheet1.Columns.Count; i++)
{
object cellvalue = oSheet1.get_Range("B:B" + Convert.ToString(i));
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value == cellvalue)
{
for (int j = 0; j < dataGridView1.Rows.Count; j++)
{
oSheet1.Cells[i, 3] = dataGridView1.Rows[j].Cells[1].Value.ToString();
}
}
}
}
I would appreciate any help. Thank you!!
Try UsedRange.
var range = oSheet1.UsedRange;
int startingRowIndex = range.Row + 1; //to skip the header
for (int rowIndex = startingRowIndex; rowIndex < range.Row + range.Rows.Count; rowIndex++)
{
string cellvalue = range.Cells[rowIndex, 2].Value?.ToString();
...
}
Also, you should perform a null check against the Value property just in case the cell is empty, or uses a null-conditional operator as shown in the code above.

How to change a Datagridviewcombboxcell by another Datagridview

I got two different Datagridviews.
Datagridview2 has just text columns. Datagridview1 contains mostly text columns and one DatagridviewComboBoxColumn. The comboxbox contains a list of all values from one column in Datagridview2.
I want to update each comboboxcell everytime Datagridview2 changes.
I’ve a read and tried a lot but i still haven’t found a way how i can change the values of a DatagridviewComboboxcell during runtime with content from another Datagridview.
To make things a bit more clear, here is a little code-snippet of how i want to change the content on a specific event (this code don’t work)
var cls = new List<string>();
if (dataGridView2.Rows.Count > 0)
{
for (int rows = 0; rows < dataGridView2.Rows.Count; rows++)
{
if (dataGridView2[0, rows].Value != null)
{
cls.Add(dataGridView2[0, rows].Value.ToString());
}
}
for (int rows = 0; rows < dataGridView1.Rows.Count; rows++)
{
DataGridViewComboBoxCell cl = new DataGridViewComboBoxCell();
var cl_content = cls;
cl.DataSource = cl_content;
var cell = dataGridView1[1, rows] as DataGridViewComboBoxCell;
cell.DataSource = cl;
}
}
I’ve tried some ideas from another post
Dynamically setting DataGridViewComboBoxCell's DataSource to filtered DataView based off of other cell selection
but nothing worked.
Maybe just copy the contents from one dataGridView to the other dataGridView when you finish editing the dataGridView.
Here is a copy dataGridViews function:
public void CopyDataGridViews(DataGridView SourceDGV, DataGridView DestinationDGV)
{
// Clear Destination DataGridView
DestinationDGV.Columns.Clear();
DestinationDGV.Rows.Clear();
// Create Destination Columns
for (int j = 0; j < SourceDGV.Columns.Count; j++)
{
DestinationDGV.Columns.Add(SourceDGV.Columns[j].Clone() as DataGridViewColumn);
}
// Create Destination Rows
for (int i = 0; i < SourceDGV.Rows.Count - 1; i++)
{
DestinationDGV.Rows.Add(SourceDGV.Rows[i].Clone() as DataGridViewRow);
}
// Copy Data to Destination
for (int column = 0; column < SourceDGV.Columns.Count; column++)
{
for (int row = 0; row < SourceDGV.Rows.Count; row++)
{
DestinationDGV.Rows[row].Cells[column].Value = SourceDGV.Rows[row].Cells[column].Value;
}
}
}

Categories