How to read table of MS Word in c# winforms? - c#

I have a table with 8 columns in MS Word. 7 columns are text based and one contains image. I want to read all values row by row and show in controls on form. I have tried following code its giving me an error. Also this code is for text I think
w = new Word.Application();
var document = w.Documents.Open(tbWordFile.Text.Trim());
for (int iCounter = 1; iCounter <= document.Tables.Count; iCounter++)
{
foreach (Row in document.Tables[iCounter].Rows)
{
foreach (Cell aCell in aRow.Cells)
{
currLine = aCell.Range.Text;
//Process Line
}
}
}
There is An error occured on "Row" variable is that "Row is inaccessible due to
its protection level

foreach (Row in document.Tables[iCounter].Rows)
you are iterating rows and your per item is what? You declare Row with no variable name. This should be something like:
foreach (Row aRow in document.Tables[iCounter].Rows)

Related

Error when trying to duplicate rows in DataTable in c#

I have an existing datatable called _longDataTable containing data. Now, I want to duplicate each row and in each duplicate of the row, I want to set only the value in the SheetCode column according to a value from a different datatable called values, see code below. For example, the values datatable contains 1, 2 and 3, then I want each row of _longDataTable to be duplicated three times and in each of the duplicated rows, I want the Sheet Code column to have values 1, 2 and 3 respectively. My code now looks like below:
foreach (DataRow sheets in _longDataTable.Rows)
{
for(int k = 0; k < number_of_sheets; k++)
{
var newRowSheets = _longDataTable.NewRow();
newRowSheets.ItemArray = sheets.ItemArray;
newRowSheets["SheetCode"] = values.Rows[k]["Sheet Code"];
//add edited row to long datatable
_longDataTable.Rows.Add(newRowSheets);
}
}
However, I get the following error:
Collection was modified; enumeration operation might not execute.
Does anyone know where this error comes from and how to solve my problem?
you get enumeration error because you are iterating through a collection which is changing in the loop(new rows added to it),
as you said in the comment, you get out of memory exception because you are iterating on the _longDataTable, then you add rows to it, the iteration never reach to end and you will get out of memory exception.
I assume this can help you:
//assume _longDataTable has two columns : column1 and SheetCode
var _longDataTable = new DataTable();
var duplicatedData = new DataTable();
duplicatedData.Columns.Add("Column1");
duplicatedData.Columns.Add("SheetCode");
foreach (DataRow sheets in _longDataTable.Rows)
{
for (int k = 0; k < number_of_sheets; k++)
{
var newRowSheets = duplicatedData.NewRow();
newRowSheets.ItemArray = sheets.ItemArray;
newRowSheets["SheetCode"] = values.Rows[k]["Sheet Code"];
newRowSheets["Column1"] = "anything";
//add edited row to long datatable
duplicatedData.Rows.Add(newRowSheets);
}
}
_longDataTable.Merge(duplicatedData);
do not modify _longDataTable, add rows to the temp table (with the same schema) and after the iteration merge two data tables.

Insert mutiple CSV files in multiple columns

Dategridview of my list of values
How could I add 3 column of values of Column 5 -10 from the 3 comma-separated-values (csv) files at one go
say : C:\FYP\2000data\Z1ert00000.cvs,
C:\FYP\2000data\Z1ert00001.cvs and
C:\FYP\2000data\Z1ert00002.cvs
when I click the add/import button?
P.S : I have 2000 cvs files to insert to the DatagridView. Is there
any other easier way too ? Means I will have 2000 columns appearing in
the Datagridview.
private void btnImport_Click(object sender, EventArgs e)
{
var parsedData = new List<string[]>();
using (var sr = new StreamReader(txtFilename.Text)) //
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] row = line.Split(',');
parsedData.Add(row);
}
}
dataGridView1.ColumnCount = 2;
for (int i = 0; i < 2; i++)
{
var sb = new StringBuilder(parsedData[0][i]);
dataGridView1.Columns[1].Name = sb.ToString();
}
foreach (string[] row in parsedData)
{
dataGridView1.Rows.Add(row);
}
for (int x = 0; x < 5; x++)
{
dataGridView1.Rows.Remove(dataGridView1.Rows[0]); // first 5 rows of the data
}
dataGridView1.Columns.Remove(dataGridView1.Columns[0]); // remove the first column
}
These are the codes to import only one csv file
Thanks in advance!! :)
What you're trying to achieve
You're saying you have 2000 files you'd like to see on the datagridview. I find it hard to believe that this is what you realy want as it will become
pretty slow on the initial load (parsing 2000 files)
Unreadable... who scrolls through 2000 columns?
So I think you should first consider what it is you want to do with the information in these csv files. Think about UI design. Perhaps create a search function?
Working with large data
If these are static files, I would propose to import all these csv files into a database so you have easy access to them and can use an ORM model in your program. Take a look at Entity Framework.
Importing these files into a SQL database can be as easy as this:
BULK INSERT SchoolsTemp
FROM 'C:\CSVData\Schools.csv'
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = ',', --CSV field delimiter
ROWTERMINATOR = '\n', --Use to shift the control to next row
TABLOCK
)
Or use any of the already available tutorials out there.
Then you can start thinking about paging the data you're getting out and how to visualize them so the data becomes useful.
Hope this helps.

How can i get actual used range for modified excels using Epplus?

I am reading data from excel to datable using EPPlus.
After reading an excel sheet with 10 rows of record, I modified the excel sheet by removing existing data and kept data for only one row.
But when I am reading the modified excel it still reading 10 rows (1 with value and remaining as null fields) to data table.
How can limit this?
I am using following code for reading Excel.
using (var pck = new OfficeOpenXml.ExcelPackage())
{
using (var stream = File.OpenRead(FilePath))
{
pck.Load(stream);
}
var ws = pck.Workbook.Worksheets.First();
bool hasHeader = true; // adjust it accordingly(this is a simple approach)
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
DSClientTransmittal.Tables[0].Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = hasHeader ? 2 : 1;
for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
//var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
var wsRow = ws.Cells[rowNum, 1, rowNum, DSClientTransmittal.Tables[0].Columns.Count];
var row = DSClientTransmittal.Tables[0].NewRow();
foreach (var cell in wsRow)
{
try
{
object cellValue = cell.Value;
//row[cell.Start.Column - 1] = cell.Text;
row[cell.Start.Column - 1] = cellValue.ToString().Trim();
//cell.Style.Numberformat.Format = "#";
//row[cell.Start.Column - 1] = cell.Text;
}
catch (Exception ex) { }
}
DSClientTransmittal.Tables[0].Rows.Add(row);
}
pck.Dispose();
}
When I was using Interop excel to read excel, same issue was overcame by
clearformat() method like
ws.Columns.ClearFormats();
xlColCount = ws.UsedRange.Columns.Count;
Is there any equivalent for this in Epplus open xml?
How can I get actual used range for modified excels?
There is no built-in way of indicating that a row shouldn't be accounted for when only deleting data in some cells.
Dimension is as close as you can get, but rows are included in the Dimension if any column contains data or if any row above or below contains data.
You could however try to find out if you should skip a row in the for loop.
For example if you always delete data in the first 4 columns only, then you could try:
if(!ws.Cells[rowNum, 1, rowNum, 4].All(c => c.Value == null))
{
//Continue adding the row to the table
}
The description isn't indicating the criteria for skipping a row, but you get the idea.
To start with, I am not a C# programmer, but I think I have a solution that works using an Excel VBA script. You may be able to run this Excel VBA code with C, or get insight in how to accomplish the same thing with C+.
The problem you are having is related to the way Excel handles the working size of a worksheet. If you enter data in the 1 millionth row and then delete that cell, Excel still shows the worksheet as having 1 million rows.
I tested out this Excel VBA code and it successfully deleted all rows that were completely empty, and then reset the worksheet size.
Sub DelEmptyRowsResizeWorksheet()
Dim i As Long, iLimit As Long
iLimit = ActiveSheet.UsedRange.Rows.Count
For i = iLimit To 1 Step -1
If Application.CountA(Cells(i, 1).EntireRow) = 0 Then
Cells(i, 1).EntireRow.Delete
End If
Next i
iLimit = ActiveSheet.UsedRange.Rows.Count ' resize the worksheet based on the last row with data
End Sub
To do this manually without a script, first delete all empty rows at the bottom (or columns on the right side) of a worksheet, save it, then close and reopen the workbook. I found that this also resets the Excel workbook size.

Reading from Excel File using ClosedXML

My Excel file is not in tabular data. I am trying to read from an excel file.
I have sections within my excel file that are tabular.
I need to loop through rows 3 to 20 which are tabular and read the data.
Here is party of my code:
string fileName = "C:\\Folder1\\Prev.xlsx";
var workbook = new XLWorkbook(fileName);
var ws1 = workbook.Worksheet(1);
How do I loop through rows 3 to 20 and read columns 3,4, 6, 7, 8?
Also if a row is empty, how do I determine that so I can skip over it without reading that each column has a value for a given row.
To access a row:
var row = ws1.Row(3);
To check if the row is empty:
bool empty = row.IsEmpty();
To access a cell (column) in a row:
var cell = row.Cell(3);
To get the value from a cell:
object value = cell.Value;
// or
string value = cell.GetValue<string>();
For more information see the documentation.
Here's my jam.
var rows = worksheet.RangeUsed().RowsUsed().Skip(1); // Skip header row
foreach (var row in rows)
{
var rowNumber = row.RowNumber();
// Process the row
}
If you just use .RowsUsed(), your range will contain a huge number of columns. Way more than are actually filled in!
So use .RangeUsed() first to limit the range. This will help you process the file faster!
You can also use .Skip(1) to skip over the column header row (if you have one).
I'm not sure if this solution will solve OP's problem but I prefer using RowsUsed method. It can be used to get the list of only those rows which are non-empty or has been edited by the user. This way I can avoid making emptiness check while processing each row.
Below code snippet can process 3rd to 20th row numbers out of all the non-empty rows. I've filtered the empty rows before starting the foreach loop. Please bear in mind that filtering the non-empty rows before starting to process the rows can affect the total count of rows which will get processed. So you need to be careful while applying any logic which is based on the total number of rows processed inside foreach loop.
string fileName = "C:\\Folder1\\Prev.xlsx";
using (var excelWorkbook = new XLWorkbook(fileName))
{
var nonEmptyDataRows = excelWorkbook.Worksheet(1).RowsUsed();
foreach (var dataRow in nonEmptyDataRows)
{
//for row number check
if(dataRow.RowNumber() >=3 && dataRow.RowNumber() <= 20)
{
//to get column # 3's data
var cell = dataRow.Cell(3).Value;
}
}
}
RowsUsed method is helpful in commonly faced problems which require processing the rows of an excel sheet.
It works easily
XLWorkbook workbook = new XLWorkbook(FilePath);
var rowCount = workbook.Worksheet(1).LastRowUsed().RowNumber();
var columnCount = workbook.Worksheet(1).LastColumnUsed().ColumnNumber();
int column = 1;
int row = 1;
List<string> ll = new List<string>();
while (row <= rowCount)
{
while (column <= columnCount)
{
string title = workbook.Worksheets.Worksheet(1).Cell(row, column).GetString();
ll.Add(title);
column++;
}
row++;
column = 1;
}

SAP Business One Automatic Row numbering for Matrix

I am making an add-on for SAP B1 8.82. I would like automatic row numbering for my matrix column "#" i.e. UID "V_-1". Is there a feature in 8.82 that can help me do this?
I am using UserDataSource for my matrix. How do I get to have row numbers that automatically update when I add/remove a row?
I have the following added to menu event 1292 for adding row numbers manually but cant get it to work for row deletion - menu event 1293. (Using C#)
case "1292"://Add Matrix Row
_form.Freeze(true);
_form.DataSources.UserDataSources.Item("itemNo").Value = "";
_form.DataSources.UserDataSources.Item("itemDesc").Value = "";
_form.DataSources.UserDataSources.Item("quantity").Value = "";
_form.DataSources.UserDataSources.Item("warehouse").Value = "";
_form.DataSources.UserDataSources.Item("distrRule").Value = "";
_form.DataSources.UserDataSources.Item("project").Value = "";
oMatrixItem.AddRow();
// row numbering
int i = 1;
int j = 0;
j = oMatrixItem.RowCount;
while (j >= i)
{
oMatrixItem.Columns.Item("V_-1").Cells.Item(i).Specific.Value = i.ToString();
i = i + 1;
}
_form.Freeze(false);
break;
In order to get the number in "V_-1" column simply
we can bind our matrix to a table and link the line ID column to "V_-1" column
we can use a DBDataSource for binding the matrix to the table and link the "V_-1" to matrix column
However if we need to maintain the serial number even after deleting the row we need to write separate code after loading the data into matrix in form load after action event.
Sample pseudo-code:
for i as integer = 1 to matrix.rowcount
matrix.columns.itm(V_-1).item().cells(i)=i
next
this is what I did after adding row to matrix.
Column name was not working for me, but index '0' was ok.
iRow is index of row where I want to update this index in # column.
SAPbouiCOM.EditText oEditID = (SAPbouiCOM.EditText) oMatrix.Columns.Item(0).Cells.Item(iRow).Specific;
oEditID.Active = true;
oEditID.Value = iRow.ToString();

Categories