How to find all named ranges in a workbook - c#

Here is some code that I am using so far:
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Open(path);
// Make the object visible.
excelApp.Visible = true;
I know the workbook opens.
I am using Visual Studio 2013.

The Workbooks.Open() function returns a workbook object which you need to store and it contains more objects with the workbook data. You should be able to get all this information from intellisense or you can find information on the Microsoft.Office.Interop.Excel namespace on MSDN.
Here is an example script which will open a message box with the names of each of the named ranges in the workbook at path.
using Microsoft.Office.Interop.Excel;
Application excelApp = new Application();
Workbook myWorkbook = excelApp.Workbooks.Open(path);
Names wbNames = myWorkbook.Names;
foreach (Name n in wbNames)
{
System.Windows.Forms.MessageBox.Show(n.Name);
}

You can also get the location of the nametag (called value):
Microsoft.Office.Interop.Excel.Names Names = wb.Names;
foreach(Microsoft.Office.Interop.Excel.Name item in Names)
{
if(item.Name.Contains("cellexportcondition"))
{
MessageBox.Show(item.Name.ToString() + " = " + item.Value.ToString());
}
}
OR you can also get the cell values (if you want to do this in the workbook.beforeclose event you will need to reïnit the app and worksheet)
Microsoft.Office.Interop.Excel.Application tempexcellApp= wb.Application;
Microsoft.Office.Interop.Excel.Worksheet tws = (Microsoft.Office.Interop.Excel.Worksheet)tempexcellApp.Worksheets[1];
Microsoft.Office.Interop.Excel.Names Names = wb.Names;
foreach(Microsoft.Office.Interop.Excel.Name item in Names)
{
if(item.Name.Contains("cellexportcondition"))
{
string a = "";
if(tws.Range[item.Name].Value2 != null)
{
a = item.Name + " " + tws.Range[item.Name].Value2.ToString();
}else a = item.Name + " empty cell ";
MessageBox.Show(a);
}
}

Related

Add hyperlink to smartart node with interop-excel in c#

I have an c# console application which create an excel worksheet containing a smartart object with the hiearchy layout(OrgChart). I would like to add hyperlinks to the nodes within the org chart, but somehow i can't.
In the picture below, i would like to add a hyperlink to "Node 1"(1) which will take me to the "LinkedSheet" sheet(2):
with the following code snippet, i tried to add a hyperlink to the sheet "orgChart" where the textFrame of "Node 1"(var name: ndTop) is the anchor and the sheet "LinkedSheet" is the target:
Sheet.Hyperlinks.Add(ndTop.TextFrame2, "", "'" + LinkSheet.Name + "'!A1", "", "");
But i get the following error:
The error translated to english:
'The remote procedure call failed. (Exception from HRESULT: 0x800706BE) '.
There are no inner exception.
I added the following references to my project:
Microsoft.Office.Interop.Excel (Nuget package).
Microsoft Office 16.0 Object Library (COM library from add reference)
The usings:
using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
The Code:
private static Excel.Workbook Wb = null;
private static Excel.Application Xl = null;
private static Excel.Worksheet Sheet = null;
private static Excel.Worksheet LinkSheet = null;
static void Main(string[] args)
{
Xl = new Excel.Application();
Xl.Visible = true;
Wb = Xl.Workbooks.Add();
LinkSheet = Wb.Worksheets[1];
LinkSheet.Name = "LinkedSheet";
Sheet = Wb.Worksheets.Add();
Sheet.Name = "OrgChart";
var myLayout = Xl.SmartArtLayouts[88];
var smartArtShape = Sheet.Shapes.AddSmartArt(myLayout, 50, 50, 200, 200);
if (smartArtShape.HasSmartArt == Office.MsoTriState.msoTrue)
{
Office.SmartArt smartArt = smartArtShape.SmartArt;
Office.SmartArtNodes nds = smartArt.AllNodes;
Office.SmartArtNode ndTop = null;
foreach (Office.SmartArtNode nd in nds)
{
if (nd.Level != 1)
{
nd.Delete();
}
else
{
ndTop = nd;
ndTop.TextFrame2.TextRange.Text = "Node 1";
}
}
//Adding the hyperlink
Sheet.Hyperlinks.Add(ndTop.TextFrame2, "", "'" + LinkSheet.Name + "'!A1", "", "");
Office.SmartArtNode ndLev2_1 = ndTop.AddNode(Office.MsoSmartArtNodePosition.msoSmartArtNodeBelow);
ndLev2_1.TextFrame2.TextRange.Text = "Node 1.1";
Office.SmartArtNode ndLev2_2 = ndTop.AddNode(Office.MsoSmartArtNodePosition.msoSmartArtNodeBelow);
ndLev2_2.TextFrame2.TextRange.Text = "Node 1.2";
Office.SmartArtNode ndLev2_3 = ndTop.AddNode(Office.MsoSmartArtNodePosition.msoSmartArtNodeBelow);
ndLev2_3.TextFrame2.TextRange.Text = "Node 1.3";
Office.SmartArtNode ndLev2_1_1 = ndLev2_1.AddNode(Office.MsoSmartArtNodePosition.msoSmartArtNodeBelow);
ndLev2_1_1.TextFrame2.TextRange.Text = "Node 1.1.1";
}
}

Writing to sheet leads to "name already taken"

I'm learning how to use Interop.Excel. The test Winforms program reads an existing Excel file, checks if a tab names "Added_by_program" exists, deletes the sheet if it does, and creates a new sheet named "Added_by_program." If I don't try to write to the new sheet, the program runs perfectly, over and over. I get problems when I try to write to it. If the sheet is not present in the original file, the program runs perfectly one time, and writes correctly to the newly created sheet. but on subsequent runs, I get:
"System.Runtime.InteropServices.COMException: 'That name is already taken. Try a different one.'"
for the line that tries to name the new sheet. I have to manually kill the open Excel instance. What am I missing?
Code (irrelevant lines taken out)
using System;
using System.IO;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
namespace excelReadWrite
{
public partial class Form1 : Form
{
string readFolder = myPath;
string inFileName = #"Aram test excel file.xlsx";
string newSheetName = "Added_by_program";
Range rawRange = null;
Range pasteRange = null;
int rawCols = 0;
int rawRows = 0;
int iInSheet = 0;
int iNewSheet = 0;
int nInSheets = 0;
bool foundRawSheet = false;
bool foundNewSheet = false;
Worksheet worksheet = null;
public Form1()
{
InitializeComponent();
}
private void start_Button_Click(object sender, EventArgs e)
{
string inFile = myPath+ inFileName;
int nSheets = 0;
string sheetNames = "";
// Open Excel workbook to read
Microsoft.Office.Interop.Excel.Application xl = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = xl.Workbooks.Open(inFile);
// Count worksheets in opened Excel file
nSheets = workbook.Worksheets.Count;
nSheets_TextBox.Text = nSheets.ToString();
nInSheets = 0;
foreach (Worksheet worksheet in workbook.Worksheets)
++nInSheets;
//foreach (Worksheet worksheet in workbook.Worksheets)
for (int iSheet = nInSheets; iSheet >= 1; --iSheet)
{
worksheet = workbook.Worksheets[iSheet];
sheetNames += " " + worksheet.Name;
// The program is going to add a worksheet. If it already exists, delete it before adding it.
if (string.Equals(worksheet.Name, newSheetName))
{
workbook.Worksheets[iSheet].Delete();
}
}
// Add a new sheet and name it
if (foundRawSheet)
{
newWorksheet = workbook.Worksheets.Add();
newWorksheet.Name = newSheetName;
// THE NEXT LINE IS THE PROBLEM LINE
// "Written" WILL BE WRITTEN TO A1:C3 WHEN THE SHEET IS CREATED, BUT THIS LINE
// CAUSES THE ERROR IN SUBSEQUENT RUNS
// IF I COMMENT IT OUT, THE PROGRAM RUNS FINE, REPEATEDLY
newWorksheet.Range["A1", "C3"].Value2 = "Written";
workbook.Save();
workbook.Close();
xl.Quit();
}
}
}
Did you set xl.DisplayAlerts=false?
If not, deleting a worksheet with existing data will cause a confirm dialog to be displayed. .
If the Excel application is visible, the Worksheet.Delete will block until the dialog is acknowledged.
If the Excel application is not visible, your code execution will proceed (the dialog is effectively canceled --> delete not confirmed), but the worksheet will not be deleted.

Invalid Index. Exception from HRESULT: 0x8002000B (DISP_E_BADINDEX) when Workbooks.Open

I am trying to make an app that will open an xlsx file for reading, and than read it and do some stuff with it. When I run my app, and click a button to load the file, I am getting this error:
Invalid Index. Exception from HRESULT: 0x8002000B (DISP_E_BADINDEX)
On this line of code:
Excel.Workbook a
= excelApp.Workbooks.Open("C:\\test.xlsx", 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows,
"\t", false, false, 0, true, 1, 0);
Can you suggest what could be wrong here?
EDIT: Here is full code so I hope it will be easier to tell what causes the error
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form, IDisposable
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Excel.Application excelApp = new Excel.Application();
excelApp.Visible = true;
Excel.Workbook a = excelApp.Workbooks.Open("C:/test.xlsx");
// This selectes the used range of the excel workbook and enters it in
// a two dimentional array
try
{
// Get a reference to the first sheet of the workbook.
Excel.Sheets excelSheets = a.Worksheets;
string currentSheet = "Sheet1";
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(currentSheet);
// write out to console for debugging
textBox1.Text = "excelWorksheet is " + excelWorksheet;
// Get a range of data.
Excel.Range excelCell = (Excel.Range)excelWorksheet.get_Range("A3", Missing.Value);
// write out to console for debugging
textBox1.Text = "excelCell is " + excelCell;
// write out to console for debugging
textBox1.Text = "Creating string[,] array. . . ";
// Retrieve the data from the range.
Object[,] dataArray;
// write out to console for debugging
textBox1.Text = "String[,] array created. . . ";
dataArray = (System.Object[,])excelCell.get_Value(Missing.Value);
// write out to console for debugging
textBox1.Text = "Counting rows and columns. . . ";
// Determine the dimensions of the array.
int iRows;
int iCols;
iRows = dataArray.GetUpperBound(0);
iCols = dataArray.GetUpperBound(1);
// write out to console for debugging
textBox1.Text = "Printing array. . . ";
// Print the data of the array.
for (int rowCounter = 1; rowCounter <= iRows; rowCounter++)
{
// write out to console for debugging
textBox1.Text = ("row " + rowCounter);
for (int colCounter = 1; colCounter <= iCols; colCounter++)
{
// Write the next value to the console.
richTextBox1.Text = "col " + colCounter + "= " + dataArray[rowCounter, colCounter].ToString() + ", ";
}
// Write in a new line.
richTextBox1.Text = "\n";
}
}
catch (Exception theException)
{
// Create error message
String errorMessage;
errorMessage = "Error: ";
errorMessage = String.Concat(errorMessage, theException.Message);
errorMessage = String.Concat(errorMessage, " Line: ");
errorMessage = String.Concat(errorMessage, theException.Source);
// Display error message
MessageBox.Show(errorMessage, "Error");
}
}
}
}
I do not know if you have found a solution, but this is a workaround. Hope it helps
I was faced with exactly the same issue.
the problem is on this line. in my case the workbook has multiple sheets, thus i wanted to loop through each and retrieve data.
string currentSheet = "Sheet1";
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(currentSheet);
you are trying to open a sheet with index/name "Sheet1" which does not exist..if you already know the sheet names you are expecting go ahead and use the names otherwise it is best if you use the sheet index.
example
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(1);
this gets the sheet at index 1
this is how i solved mine. i first check if the sheet name exists
var sheetExists = xlWorkBook.Worksheets.Cast<Excel.Worksheet>().FirstOrDefault(worksheet => worksheet.Name == "SomeSheetName"); // this line returns null if the sheet name or index you intend to open does not exist
if (sheetExists != null) // this line thus handles the invalid index error.
{ /**you can now open the sheet**/
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(currentSheet);
}
I believe the sheetExists variable already has the worksheet so no need to get it again, so if sheetExists is not null then is ready to be utilized

How to create(export) excel file readonly using Open Xml 2.5 SDK

I am developing one WPF application (using MVVM). I am also able to export my List to Excel file.But my problem is I am not able to make it readonly, so no one can able to make changes afterwards.
document.WorkbookPart.Workbook.WorkbookProtection=new WorkbookProtection
{
LockStructure=true
};
I only found this, but it's only make workbook readonly
Here is my code for creating excel file
public static bool CreateExcelDocument(DataSet ds, string excelFilename)
{
try
{
using (SpreadsheetDocument document = SpreadsheetDocument.Create(excelFilename, SpreadsheetDocumentType.Workbook))
{
document.AddWorkbookPart();
document.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
document.WorkbookPart.Workbook.WorkbookProtection=new WorkbookProtection
{
LockStructure = true,
};
// My thanks to James Miera for the following line of code (which prevents crashes in Excel 2010)
document.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView()));
// If we don't add a "WorkbookStylesPart", OLEDB will refuse to connect to this .xlsx file !
WorkbookStylesPart workbookStylesPart = document.WorkbookPart.AddNewPart<WorkbookStylesPart>("rIdStyles");
Stylesheet stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet = stylesheet;
CreateParts(ds, document);
}
Trace.WriteLine("Successfully created: " + excelFilename);
return true;
}
catch (Exception ex)
{
Trace.WriteLine("Failed, exception thrown: " + ex.Message);
return false;
}
}
private static void CreateParts(DataSet ds, SpreadsheetDocument spreadsheet)
{
// Loop through each of the DataTables in our DataSet, and create a new Excel Worksheet for each.
uint worksheetNumber = 1;
foreach (DataTable dt in ds.Tables)
{
// For each worksheet you want to create
string workSheetID = "rId" + worksheetNumber.ToString();
string worksheetName = dt.TableName;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
// create sheet data
newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData());
// save worksheet
WriteDataTableToExcelWorksheet(dt, newWorksheetPart);
newWorksheetPart.Worksheet.Save();
// create the worksheet to workbook relation
if (worksheetNumber == 1)
spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
spreadsheet.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet()
{
Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart),
SheetId = (uint)worksheetNumber,
Name = dt.TableName
});
worksheetNumber++;
}
spreadsheet.WorkbookPart.Workbook.Save();
}
Before you open Excel, you can Lock your file with:
FileInfo cInfo = new FileInfo(Path);
cInfo.IsReadonly = true;
Than it will be readonly.

DateTimePicker value from Excel

I am wondering if it is possible to set the value of a DateTimePicker from a queried row in excel. I have tried different ways of doing it but have never came close and anyone help?
Also it all is displayed on a windows form
public void ReadAndWriteToExcel()
{
string myPath = #"C:\Excel.xls";
FileInfo fi = new FileInfo(myPath);
if (!fi.Exists)
{
Console.Out.WriteLine("file doesn't exists!");
}
else
{
var excelApp = new Microsoft.Office.Interop.Excel.Application();
var workbook = excelApp.Workbooks.Open(myPath);
Worksheet worksheet = workbook.ActiveSheet as Worksheet;
// To write to excel
Microsoft.Office.Interop.Excel.Range range = worksheet.Cells[1, 1] as Range;
DateTime dt = dateTimePicker1.Value;
range.NumberFormat = "dd/MMM/yyyy";
range.Value2 = dt;
// To read,
var date = worksheet.Cells[1, 1].Value;
Console.Out.WriteLine(date.ToString());
workbook.Save();
workbook.Close();
}
}
you must add "Microsoft.Office.Core" and "Microsoft.Office.Interop.Excel" references to your project and use them.

Categories

Resources