I'm creating two worksheets with openxml, I want to add a hyperlink from a cell in sheet 2 to a cell in sheet 1. I know that with Microsoft interop excel, you could use a match function to find the row number where a value is found. I was wondering how could the same task be done with openXML instead.
With Microsoft interop excel, I did something like this
var excelApp = new Application();
WorksheetFunction function = excelApp.WorksheetFunction;
var rowNumber = function.Match(currentItem.originID, sheet1.Range["B1","B" + rowCount], 0);
Where sheet1 is the sheet I'm searching through, and rowCount is how many rows there are in that sheet. This would find currentItem.originID in the range of column B and return the row number that the match was found
How could I do something similar to this but with openXML?
Related
I am trying to count the number of sheets in a workbook. The workbook is created using NPOI and there doesn't seem to be a way to count the amount of sheets using the C# version of NPOI?
This is a really tricky thing to both explain and show... But I will give it a try.
What I am trying to do is having an existing excel-file as a template for statistics. This existing excel-file can have different amounts of templates and I need to be able to count these templates to know where to place my new sheets and edit their names.
The sender of the data only has to chose which template-sheet should be filled with which data, and I will then remove the template-sheets from the workbook after all data has been inserted.
What I have tried:
I have read the documentation and searched for information and have tried the following approaches:
getNumberOfSheets - How to know number of sheets in a workbook?
Problem with this approach: The C# version of NPOI doesn't seem to have getNumberOfSheets.
Convert found row-counters into sheet-counters - NPOI - Get excel row count to check if it is empty
Can't really recreate the code to work for sheets as the functionality for sheets and rows are too different.
var sheetIndex = 0;
foreach (var sheet in requestBody.Sheets)
{
if (sheet.TemplateNumber == "")
{
sheetTemplate = templateWorkbook.CreateSheet(sheet.Name);
}
else
{
sheetTemplate = templateWorkbook.CloneSheet(Convert.ToInt32(sheet.SheetTemplate));
if (!templates.Contains(Convert.ToInt32(sheet.SheetTemplate)))
{
templates.Add(Convert.ToInt32(sheet.SheetTemplate));
}
// Do math's to make sure we add the name to the newly created sheet further down the code (I need to actual index here)
}
// Insert statistics
//After inserting statistics:
workingCopy.SetSheetName(sheetIndex, sheet.Name);
foreach (var template in templates)
{
workingCopy.RemoveSheetAt(template);
}
}
You can get number of sheets from NumberOfSheets property in XSSFWorkbook class.
I am trying to insert a formula into an excel column using EPPlus and evaluate it in Excel. To be clear: no, I don't need the result at the runtime of the program.
This is my code:
using (ExcelRange range = worksheet.Cells[1, 1, rowCounter - 1, worksheet.Dimension.End.Column])
{
ExcelTable table = worksheet.Tables.Add(range, $"someName");
table.ShowHeader = true;
// insert calculated column
ExcelTableColumn column = table.Columns[0];
column.Name = "Remaining Runtime";
column.CalculatedColumnFormula = "=DAYS([DateOfRepayment],TODAY())";
}
The range includes a column with the header DateOfRepayment, the formula works if I select a field and then deselect it or when I just select the formula in the cell in Excel and hit enter. Before these steps, I have an Invalid Name Error referencing the [DateOfRepayment]. However, using just =[DateOfRepayment] as the formula works fine.
How can I insert this formula in EPPlus so that it will work in Excel?
(I am using EPPlus version 4.5.1)
At the moment of this answer, the formula you are referring "=DAYS()" is not supported by EPPlus. I know you are not looking for the result at the runtime of the program. But you can either replace that formula by the one that is supported by EPPlus or generate result at runtime.
https://github.com/JanKallman/EPPlus/wiki/Supported-Functions
I have found two ways those were being used by people if we want to read or write to a cell in an excel.
Declaration:
Excel.Application ExcelApp = new Excel.Application();
Excel.Workbook srcWorkBook = ExcelApp.Workbooks.Open(#"C:\test.xls");
Excel.Worksheet srcWorkSheet = srcWorkBook.Worksheets[1];
Excel.Range srcRange = srcWorkSheet.UsedRange;
Usage 1:
srcWorkSheet.Cells[1, 1].value2 = "foo bar";
Usage 2:
srcRange.Cells[2, 2].Value2 = "foo bar";
Which one is the best way to use ? or it's all fine in .NET ?
The two ways are very different.
srcWorkSheet.Cells[1, 1].value2 = "foo bar";
The 1. usage refers to the A1 cell of the first worksheet.
MSDN Worksheets.Cells property Excel
srcRange.Cells[2, 2].Value2 = "foo bar";
The 2. usage takes the cell on the 2. row, 2. column of the UsedRange. If the UsedRange in Excel is B2:D5, it would put value at C3:
MSDN Worksheets.Range Property Excel
Having a variable named Range as the Range class is really not a great idea. The same goes for WorkSheet and WorkBook.
Either option will work, but they get messy as soon as you have to modify the layout of the worksheet.
If it's an option, I like to add named ranges. This works particularly well if you're using a template where you can modify the "starting" state of the workbook.
Let's say you have a series of columns, and one of them contains the "foo" value. You can add a name like "fooColumn" to the entire column or the top cell in the column.
Then you can get the column number using
worksheet.Range("fooColumn").Column
That protects you from having to manually edit column or row numbers all over the place if you move elements around on your worksheet.
And to second what others have said, use EPPlus instead of Interop. You can still use named ranges. But EPPlus is good and Interop can bring all sorts of pain when COM objects aren't released. Interop automates an application that manipulates the file. EPPlus just works with the file.
I'm working on a card number masking process. We get these human created excel documents in and need to mask a set of the digits, but its not always guaranteed that Column D is going to be the column with the card numbers. Could be Column D only, or D and G, etc. I know these documents will always have at least 10 rows not counting headers.
I want to run a scan of the worksheets in an excel workbook and detect which columns have data, then check the 3rd cell of each non null column. If it matches a numerical string at least 9 digits long, define that column as a card type in an array, then go back and iterate through the array of columns meeting that requirement and mask the desired characters. Is this reasonably doable between some C# methods and excel properties within the Interops library?
Yes, it is possible to do so. There are several libraries out there, that give you access to Excel documents and let you scan through worksheets, rows, columns and cell values.
Some libraries are based on the Interop COM interface Excel and start a background Excel process that does the real work of extracting the information.
Libraries like NPOI (for xls and xlsx) or the Open XML SDK (xlsx) access Excel files directly without the need of having Excel installed. This is extremly valuable for server side processing of Office documents. In NPOI sweeping through a Excel file looks like this (just to give you an idea).
var workbook = new XSSFWorkbook(dataStream);
var sheet = workbook.GetSheetAt(0);
var rowEnumerator = sheet.GetRowEnumerator();
while (rowEnumerator.MoveNext())
{
IRow row = (XSSFRow)_rowEnumerator.Current;
int colCount = row.LastCellNum;
var tableRow = new TableRow(colCount);
for (var c = 0; c < colCount; c++)
{
var cell = row.GetCell(c);
if (cell != null)
{
if (IsCreditCardNumber(c))
{
...
}
}
}
}
How to change index of columns in MS excel using interop?
Say I want to move column C to column A position
I wonder how to do this programatically using excel interop
Try This..
// cut column c and insert into A, shifting columns right
Excel.Range copyRange = xlWs.Range["C:C"];
Excel.Range insertRange = xlWs.Range["A:A"];
insertRange.Insert(Excel.XlInsertShiftDirection.xlShiftToRight, copyRange.Cut());