Get all worksheet names in plaintext from Excel with C# Interop? - c#

I'm using VS2010 + Office Interop 2007 to attempt to get a few specific spreadsheet names from an Excel spreadsheet with 5-6 pages. All I am doing from there is saving those few spreadsheets I need in a tab delimited text file for further processing. So for the three spreadsheet names I get, each one will have its own tab delimited text file.
I can save a file as tab delimited just fine through Interop, but that's assuming I know what the given page name is. I have been informed that each page name will not follow a strict naming convention, but I can account for multiple names like "RCP", "rcp", "Recipient", etc when looking for a desired name.
My question is, can I get all spreadsheet page names in some sort of index so I may iterate through them and try to find the three names I need? That would be so much nicer than trying to grab "RCP", "rcp", "Recipient" pages via a bajillion try/catches.
I'm close, because I can get the COUNT of pages in an Excel spreadsheet via the following:
Excel.Application excelApp = new Excel.Application(); // Creates a new Excel Application
excelApp.Visible = true; // Makes Excel visible to the user.
// The following code opens an existing workbook
string workbookPath = path;
Excel.Workbook excelWorkbook = null;
try
{
excelWorkbook = excelApp.Workbooks.Open(workbookPath, 0,
false, 5, "", "", false, Excel.XlPlatform.xlWindows, "", true,
false, 0, true, false, false);
}
catch
{
//Create a new workbook if the existing workbook failed to open.
excelWorkbook = excelApp.Workbooks.Add();
}
// The following gets the Worksheets collection
Excel.Sheets excelSheets = excelWorkbook.Worksheets;
Console.WriteLine(excelSheets.Count.ToString()); //dat count
Thank you for your time.

foreach ( Worksheet worksheet in excelWorkbook.Worksheets )
{
MessageBox.Show( worksheet.Name );
}
You could use a dictionary:
Dictionary<string, Worksheet> dict = new Dictionary<string, Worksheet>();
foreach ( Worksheet worksheet in excelWorkbook.Worksheets )
{
dict.Add( worksheet.Name, worksheet );
}
// accessing the desired worksheet in the dictionary
MessageBox.Show( dict[ "Sheet1" ].Name );

Related

work with .xlsm c#

I tried to use Spire.Xls library, but it does not support .xlsm and when i convert it to .xlsx hasn`t saved it, same with Microsoft.Office.Excel.Interop.
private void button1_Click(object sender, EventArgs e)
{
//string xlsm = #"D:\foot_Regular B07BNJ56GV B07BNK8S3Q B07BMX2NN4 with3.xlsm";
string xlsx = #"D:\foot_Regular B07BNJ56GV B07BNK8S3Q B07BMX2NN4 with3.xlsx";
//ConverXlsmToXlsx(xlsm, xlsx);
//string xlsx = #"D:\1.xlsx";
/* Load Excel File */
Excel.Application excelApp = new Excel.Application();
Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(xlsx, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
/* Load worksheets collection */
Excel.Sheets excelSheets = excelWorkbook.Worksheets;
/* Select first worksheet */
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets[1];
/* Deleting first 87 Rows */
Excel.Range range = excelWorksheet.get_Range("1:87").EntireRow;
range.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
/* Save File */
excelWorkbook.SaveAs(#"D:\out_file.xlsx");
excelWorkbook.Close(false);
excelApp.Application.Quit();
/* Release COM objects otherwise Excel remain running */
releaseObject(range);
releaseObject(excelWorkbook);
releaseObject(excelWorksheet);
releaseObject(excelApp);
MessageBox.Show("Finished");
}
Conver function:
public static void ConverXlsmToXlsx(string path, string outputPath)
{
byte[] byteArray = File.ReadAllBytes(path);
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
using (SpreadsheetDocument spreadsheetDoc = SpreadsheetDocument.Open(stream, true))
{
// Change from template type to workbook type
spreadsheetDoc.ChangeDocumentType(SpreadsheetDocumentType.Workbook);
}
File.WriteAllBytes(outputPath, stream.ToArray());
}
}
How i can easy work with .xlsm files via C#?
Help please with it, I would really appreciate it.
EPPlus seems to be the library to use.
You don't need Interop or an actual installation of MS Excel.
They have a sample how to work with VBA macros, but it seems your problem was just reading and saving files without touching the macros, so you should be good with their basic samples:
using (ExcelPackage package = new ExcelPackage(newFile))
{
// make your modifications
package.Save();
}
I personally use ClosedXML.
ClosedXML makes it easier for developers to create Excel 2007+ (.xlsx, .xlsm, etc) files. It provides a nice object oriented way to manipulate the files (similar to VBA) without dealing with the hassles of XML Documents. It can be used by any .NET language like C# and VisualBasic.NET.
You can find more details here.
Spire.XLS supports .xlsm files, here is the code to directly delete rows from a xlsm file with it:
Workbook workbook = new Workbook();
workbook.LoadFromFile("Input.xlsm");
Worksheet sheet = workbook.Worksheets[0];
sheet.DeleteRow(1,87);
workbook.SaveToFile("Output.xlsm", ExcelVersion.Version2007);
I use the Spire.XLS Pack(Hotfix) Version:8.6.6

C# Excel forces ReadOnly with no Edit Priveledges

I have an Excel Workbook ("test.xlsx") that has a cell named "gv_epxsize". My goal is to open the excel workbook and write down the string "101" in the cell with the name "gv_epxsize".
The problem is that my code keeps making the file Read Only, so it will not write the string "101" to the named cell. I keep receiving a windows prompt that I already have the file open with Windows privileges and asks if I want to open a Read Only copy. The problem is that I don't have the file open, and there is no EXCEL.EXE process running before I initiated the code.
What I've already done:
I've saved the file as a different name in a different folder - still no luck.
The file is not saved in a shared folder.
I have made sure my TaskManager has every EXCEL.EXE process killed before I run my code.
Can someone please show me what I'm doing wrong here?
Here's the snippet of C#
string filePath = "C:\\Users\\ussatdafa\\Desktop\\Work\\Projects\\test.xlsx";
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
if (xlApp == null)
{
MessageBox.Show("Excel has not been properly installed");
}
else
{
//string fileName = Path.Combine(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), filePath);
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Workbooks wbs = excelApp.Workbooks;
Workbook wb = wbs.Open(filePath, 0, false, 5, "", "", false, XlPlatform.xlWindows, "", true, false, 0, true, false, false);
Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets.get_Item(1);
wb.Names.Item("gv_epxsize").RefersToRange.Value = "101";
}
I'm honestly not sure what's going on, but I have two observations:
You are opening Excel twice -- that appears to be certain. One instance is xlApp and the other is excelApp. If you open your task manager on the instantiation of excelApp I'm pretty sure you will find two instances of Excel running. I have no idea if that's contributing to your issue or not
I always found it helpful, when debugging, to make Excel visible. You can always comment that line out before you deploy, but during debugging it's nice since exposing COM objects during debugging isn't as helpful as native .NET objects -- this way you can see it in its native form
I ran a version of your code with these changes and had no issues changing the cell value of the named range "gv_epxsize."
string filePath = "C:\\Users\\ussatdafa\\Desktop\\Work\\Projects\\test.xlsx";
Microsoft.Office.Interop.Excel.Application excelApp =
new Microsoft.Office.Interop.Excel.Application();
if (excelApp == null)
{
MessageBox.Show("Excel has not been properly installed");
}
else
{
excelApp.Visible = true;
Workbook wb = excelApp.Workbooks.Open(filePath, 0, false, 5, "", "", false,
XlPlatform.xlWindows, "", true, false, 0, true, false, false);
Worksheet ws = wb.Sheets[1];
wb.Names.Item("gv_epxsize").RefersToRange.Value = "101";
}
This worked flawlessly for me. Here is proof:
So that leads me to several possibilities:
(Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets.get_Item(1);
Isn't what you think it is. Or perhaps, it's not the same as what I used, which was:
Worksheet ws = wb.Sheets[1];
Which will return the first worksheet ("Sheet1" on a new workbook).
And of course, there is the possibility that having two Excels open is causing issues.
I am pretty sure your issue is with WHERE in your code you are saving the file. From your posted code, there is one thing that does not really work and could explain your problem. Take a look at the following snippet:
else {
//string fileName = Path.Combine(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath), filePath);
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Workbooks wbs = excelApp.Workbooks;
Workbook wb = wbs.Open(filePath, 0, false, 5, "", "", false, XlPlatform.xlWindows, "", true, false, 0, true, false, false);
Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets.get_Item(1);
wb.Names.Item("gv_epxsize").RefersToRange.Value = "101";
}
This is the else portion of the if (xlApp == null) statement. First in this else portion you create a NEW Excel application called excelApp with:
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Why you are doing this is unknown since you have already created one earlier called xlApp. But this is still ok; you are allowed to open more than one Excel application.
Then open the file and make changes:
Workbooks wbs = excelApp.Workbooks;
Workbook wb = wbs.Open(filePath, 0, false, 5, "", "", false, XlPlatform.xlWindows, "", true, false, 0, true, false, false);
Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets.get_Item(1);
wb.Names.Item("gv_epxsize").RefersToRange.Value = "101";
You open a workbook using the filePath and call the woorkbook wb. Then grab the first sheet in wb and assign it to a worksheet variable ws. Keep in mind you OPEN the workbook in this else clause. Make the changes to the workbook and then exit the else clause. All seems OK EXCEPT… since we have exited the else clause... excelApp, wbs, wb and ws variables NO LONGER EXIST. Even if you tried to save or close the file outside this else clause you will have no way to reference it because you CREATED those variables inside the else clause.
Since you can not create a new variable named wb if it already exist... Then it is clear the workbook wb is never getting saved or closed.
I put the following lines INSIDE your else clause and was able to open the file correctly even with your program running. It is clear you may be saving and closing the file somewhere later in your code but it’s not the file you opened in this else clause.
else {
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Workbooks wbs = excelApp.Workbooks;
Workbook wb = wbs.Open(filePath, 0, false, 5, "", "", false, XlPlatform.xlWindows, "", true, false, 0, true, false, false);
Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Worksheets.get_Item(1);
wb.Names.Item("gv_epxsize").RefersToRange.Value = "101";
wb.Save();
wb.Close();
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
}
Hope this makes sense.

Excel Interop Worksheets list has extra sheets, UsedRange has no data

I'm using the Excel Office Interop from C#. (In VS 2013). I'm working with an Excel workbook that was created in the version of Excel that came with Office 2010 Professional Plus. The workbook has an .xlsx extension.
When I open the workbook in Excel, it has 4 sheets visible, but when I open it using the interop, the Worksheets collection contains sheets with a bunch of other names, like these:
Icons
International URLs
International Settings
DropdownSizer
International Translastions
Data Validation
International Data
Then after that list, the sheets that are visible in the tabs of the workbook finally appear. But when I try to get at the UsedRange for any of the sheets, it has no data in it.
My code works fine for other workbooks created from the same Office/Excel installation, which only have single sheets.
Here are some mashed together snippets from my code with comments, null checks, debugging code, etc. stripped out:
private Excel.Application xlApp;
private Excel.Workbook xlWorkbook;
private Excel.Worksheet xlWorksheet;
public Excel.Range usedRange;
public void Open(string pathname, int sheetNum = 1) {
xlApp = new Excel.Application();
xlWorkbook = xlApp.Workbooks.Open(pathname, 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xlWorksheet = (Excel.Worksheet)xlWorkbook.Sheets.get_Item(sheetNum);
usedRange = xlWorksheet.UsedRange;
}
public string[] GetSheetNames() {
var sheetNames = new List<string>();
foreach (Excel.Worksheet worksheet in xlApp.Worksheets) {
sheetNames.Add(worksheet.Name);
}
return sheetNames.ToArray();
}
public void SetCurrentSheet(int sheetNum) {
usedRange = xlWorkbook.Worksheets[sheetNum].UsedRange;
}
I've tried using the xlWorkbook.Worksheets instead of xlApp.Worksheets. No difference.
How can I get just the sheets that show up in the tabs when I open the workbook in Excel?
After doing that, how can I get at the data in each sheet?
If you need to filter your sheetNames list to only include visible sheets, you can try the following:
foreach (Excel.Worksheet worksheet in xlApp.Worksheets)
{
if (worksheet.Visible == XlSheetVisibility.xlSheetVisible)
{
sheetNames.Add(worksheet.Name);
}
}
I've tried using the xlWorkbook.Worksheets instead of
xlApp.Worksheets. No difference.
The documentation says that the Application.Worksheets property returns a collection of all the worksheets in the active workbook. Since the currently active workbook is the same object as the one referenced by your xlWorkbook variable, there can't be any difference.

How to output Excel.workbook content into existing Excel file from C# application?

I have a macro-enabled Excel file "D:\MyTests\ExcelTests\template.xlsm" with no data in it, only the VBA code, and my C# code needs to output a workbook data over there. Normally I output workbook data like this:
Excel.Application application = new Excel.Application();
Excel.Workbook workbook = application.Workbooks.Add();
Excel.Worksheet worksheet = workbook.Sheets[1];
Excel.Worksheet worksheet2 = workbook.Sheets[2];
// populate worksheets with some data
DataTable2Worksheet(tableMain, worksheet, verSize);
DataTable2Worksheet(tableExtra, worksheet2, 0);
string fileName = #"D:\MyTests\ExcelTests\newFile";
if (File.Exists(fileName ))
{
File.Delete(fileName );
}
workbook.SaveAs(fileName);
workbook.Close();
Marshal.ReleaseComObject(application);
but this creates a new file (which cannot be macros enabled programmatically). If I want to output the workbook to existing file
string existingFile = #"D:\MyTests\ExcelTests\template.xlsm"
the method
workbook.SaveAs(existingFile );
won't work. So, what should I do instead? Thanks.
Save the file specifically in xlOpenXMLWorkbookMacroEnabled format:
string existingFile = #"D:\MyTests\ExcelTests\template.xlsm"
workbook.SaveAs(existingFile, 52);

Extracting a URL from hyperlinked text in Excel cell

I have a table full of Hyperlinked text in excel, so it's basically a bunch of names but when I click on one, it takes me to some URL in my default browser.
So I am extracting text from this excel table in my program, but the value I get when I extract from these hyperlink cells is that of the string inside, when I want the URL the string is linked to in the excel file.
So I'm thinking there are two ways to do this. Either I can convert all the hyperlinked text in the excel file to the corresponding URLs, or I can use C# to somehow extract the URL value from the cell and not the text.
I don't know how to do either of these things, but any help would be greatly appreciated.
C# code so far:
Excel.ApplicationClass excelApp = new Excel.ApplicationClass();
//excelApp.Visible = true;
Excel.Workbook excelWorkbook =
excelApp.Workbooks.Open("C:\\Users\\use\\Desktop\\list.xls",
0, false, 5, "", "",false, Excel.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
Excel.Sheets excelSheets = excelWorkbook.Worksheets;
string currentSheet = "Sheet1";
Excel.Worksheet xlws = (Excel.Worksheet)excelSheets.get_Item(currentSheet);
string myString = ((Excel.Range)xlws.Cells[2, 1]).Value.ToString();
As for the excel file, it's just one long row of names hyperlinked. For instance cell A2 would contain the text:
Yummy cookie recipe
And I want to extract the string:
http://allrecipes.com//Recipes/desserts/cookies/Main.aspx
You could use a vba macro:
Hit Alt+F11 to open the VBA editor and paste in the following:
Function URL(rg As Range) As String
Dim Hyper As Hyperlink
Set Hyper = rg.Hyperlinks.Item(1)
URL = Hyper.Address
End Function
And then you can use it in your Worksheet, like this:
=URL(B4)
In your code just add
string myString = ((Excel.Range)xlws.Cells[2, 1]).Cells.Hyperlinks[1].Address;
I obviously recommend doing some checks before accessing the "Hyperlinks" property.
VBA function:
Hit Alt+F11 (Opens Visual Basic Editor)
Click on Insert -> Module (adds a module to your excel file)
Paste the code below for the function of GETURL
Hit Alt+Q (Closes the Visual Basic Editor)
Now use the =GETURL(cell) to get the URL
Example: =GETURL(A1) will return the URL for the Hyperlink displayed in cell A1
Function GETURL(HyperlinkCell As Range)
GETURL = HyperlinkCell.Hyperlinks(1).Address
End Function
Source
Use Visual Studio Tools for Office (VSTO) to open Excel workbook and extract all hyperlinks.
I put a hyperlink into A1 of Sheet1 in Book1.xlsx: text = "example.com, address = "http://www.example.com"
_Application app = null;
try
{
app = new Application();
string path = #"c:\temp\Book1.xlsx";
var workbook = app.Workbooks.Open(path, 0, true, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
var sheets = workbook.Worksheets;
var sheet = (Worksheet)sheets.get_Item("Sheet1");
var range = sheet.get_Range("A1", "A1");
var hyperlinks = range.Cells.Hyperlinks.OfType<Hyperlink>();
foreach (var h in hyperlinks)
{
Console.WriteLine("text: {0}, address: {1}", h.TextToDisplay, h.Address);
}
}
finally
{
if (app != null)
app.Quit();
}
Output:
text: example.com, address: http://www.example.com/
why not use Uri class to convert string into URL:
Uri uri = new Uri("http://myUrl/test.html");
You can use VBA code to achieve this.
Press Alt + F11 to open VB editor, Insert a Module and paste the code below:
Sub run()
On Error Resume Next
For Each hLink In Selection
Range(hLink.Address).Offset(0, 1) = hLink.Hyperlinks(1).Address
Next
End Sub
Save your excel file[in excel 2007 and above save as macro enabled...]
Try this:
Excel.Application appExcel = new Excel.Application();
Excel.Workbooks workBooks = appExcel.Workbooks;
Excel.Workbook excelSheet = workBooks.Open("......EditPath", false, ReadOnly: true);
foreach (Excel.Worksheet worksheet in excelSheet.Worksheets)
{
Excel.Hyperlinks hyperLinks = worksheet.Hyperlinks;
foreach (Excel.Hyperlink lin in hyperLinks)
{
System.Diagnostics.Debug.WriteLine("# LINK: adress:" + lin.Address);
}
}
I just ran into this issue and this is what worked for me:
I used the FormulaR1C1 extension method for a range. So my code looked like this:
for (int r = 2; r <= sheetRange.Rows.Count; r++)
{
documentRecord = new List<string>();
for (int c = 1; c <= wkCol; c++)
{
documentRecord.Add(sheetRange.Cells[r, c].FormulaR1C1);
}
AllRecords.Add(documentRecord);
}
When the record is added to the list of records, the value of whatever the cell range was is formatted into a clickable-hyperlink.

Categories

Resources