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";
}
}
Related
I create a pivot table based on an Excel (.xlsx) file. The program adds fields to rows, values, and the filter. Using PivotFilters.Add2 causes 0x800a03ec error which terminates the program. How to properly use PivotFilters.Add2?
I tried filtering on different fields with different data types. Also, I tried using Type.Missing in a place of unused arguments. There seems to be plenty of information of this method for VB, but not so much for C#.
Items selected in the filter should be between the two dates on the last line.
var xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.Visible = true;
var workBook = xlApp.Workbooks.Open(spreadsheetLocation);
var workSheet1 = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets["Data"];
var workSheet2 = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets.Add();
Microsoft.Office.Interop.Excel.Range last = workSheet1.Cells.SpecialCells(Microsoft.Office.Interop.Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
Microsoft.Office.Interop.Excel.Range range = workSheet1.get_Range("A1", last);
Microsoft.Office.Interop.Excel.PivotCaches pivotCaches = null;
Microsoft.Office.Interop.Excel.PivotCache pivotCache = null;
Microsoft.Office.Interop.Excel.PivotTable pivotTable = null;
Microsoft.Office.Interop.Excel.PivotFields pivotFields = null;
Microsoft.Office.Interop.Excel.PivotField filterField = null;
Microsoft.Office.Interop.Excel.PivotField accNumField = null;
Microsoft.Office.Interop.Excel.PivotField amountPaidField = null;
pivotCaches = workBook.PivotCaches();
pivotCache = pivotCaches.Create(XlPivotTableSourceType.xlDatabase, range);
pivotTable = pivotCache.CreatePivotTable(workSheet2.Cells[1,1]);
pivotFields = (Microsoft.Office.Interop.Excel.PivotFields)pivotTable.PivotFields();
amountPaidField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AmountPaid");
amountPaidField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlDataField;
amountPaidField.NumberFormat = "$#,###,###.00";
accNumField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AccNumber");
accNumField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlRowField;
filterField = (Microsoft.Office.Interop.Excel.PivotField)pivotFields.Item("AccDate");
filterField.Orientation = Microsoft.Office.Interop.Excel.XlPivotFieldOrientation.xlPageField;
filterField.EnableMultiplePageItems = true;
filterField.PivotFilters.Add2(XlPivotFilterType.xlDateBetween, Type.Missing, DateTime.Now.AddDays(-30), DateTime.Now.AddDays(-20));
#First it deletes two rows and then it creates a pivot table#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Office.Interop.Excel;
namespace ConsoleApplication4
{
class Program
{
public static string Column(int column)
{
column--;
if (column >= 0 && column < 26)
return ((char)('A' + column)).ToString();
else if (column > 25)
return Column(column / 26) + Column(column % 26 + 1);
else
throw new Exception("Invalid Column #" + (column + 1).ToString());
}
static void Main(string[] args)
{
try
{
string path = #"C:\Users\UX155512\Documents\Book1fd.xlsx";
var excelFile = new Application();
Workbook workBook = excelFile.Workbooks.Open(path);
Worksheet workSheet = workBook.Worksheets[1];
Worksheet pivotSheet = workBook.Worksheets.Add(
System.Reflection.Missing.Value,
workBook.Worksheets[workBook.Worksheets.Count],
1,
System.Reflection.Missing.Value);
Range last = workSheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);
int lastRow = last.Row;
int lastCol = last.Column;
string lastColVal = Column(lastCol);
string lastFilledCol = lastColVal + lastRow.ToString();
Console.WriteLine(lastFilledCol);
Console.WriteLine(lastColVal);
Console.WriteLine(lastRow);
Console.WriteLine(lastCol);
for (int i = 1; i <= lastRow; i++)
{
if (workSheet.Cells[1][i].Value == ("HDR"))
{
workSheet.Rows[i].Delete();
Console.WriteLine("The Row Containing HDR has been deleted");
}
if (workSheet.Cells[1][i].Value == ("TRL"))
{
workSheet.Rows[i].Delete();
Console.WriteLine("The Row Containing TLR has been deleted");
}
}
Range lastRange = workSheet.Range["A1", lastFilledCol];
pivotSheet.Name = "Pivot Table";
Range range = pivotSheet.Cells[1, 1];
PivotCache pivotCache = (PivotCache)workBook.PivotCaches().Add(XlPivotTableSourceType.xlDatabase, lastRange);
PivotTable pivotTable = (PivotTable)pivotSheet.PivotTables().Add(PivotCache: pivotCache, TableDestination: range);
PivotField pivotField = (PivotField)pivotTable.PivotFields("Plan Number");
pivotField.Orientation = XlPivotFieldOrientation.xlRowField;
PivotField pivotField2 = (PivotField)pivotTable.PivotFields("Source");
pivotField2.Orientation = XlPivotFieldOrientation.xlColumnField;
PivotField pivotField3 = (PivotField)pivotTable.PivotFields("Total");
pivotField3.Orientation = XlPivotFieldOrientation.xlDataField;
pivotField3.Function = XlConsolidationFunction.xlSum;
workBook.SaveAs(#"C:\Users\UX155512\Documents\Excel Dump\Trial9.xlsx");
workBook.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}
}
}
As mentioned above by Asger, the filter can't be added to a page field. Instead, the pivot item's visibility property has to be set.
var pivotItems = filterField.PivotItems();
DateTime date = Convert.ToDateTime(item.Name);
foreach (var item in pivotItems)
{
item.Visible = false;
if (date < DateTime.Now.AddDays(-30) || date > DateTime.Now.AddDays(-20))
{
item.Visible = true;
}
}
The best way to find the answer to pretty much ANY interop question is to record a macro then examine the source.
I'm using C# Interop to get the Values from an Excel Worksheet depending on a parameter passed to a function and I get the following error:
the name 'sheet' does not exist in the current context
This is my code:
public void getIndexes(int num)
{
var wb = (Excel.Workbook)Globals.ThisAddIn.Application.ActiveWorkbook;
var wsEvars = wb.Sheets["Evars"];
var wsEvents = wb.Sheets["Events"];
if (num == 0)
{
var sheet = wsEvars;
}
if (num == 2)
{
var sheet = wsEvents;
}
if (num != 2)
{
var rng = (Excel.Range)sheet.Range[sheet.Cells[3, 2], sheet.Cells[3, 25]];
}
}
I suppose that the sheet variable should be initialized before the first if statement...but which should be the type of this variable since it's a COM Object?
The type you're looking for is Excel.Worksheet.
As you correctly surmised you can just declare it before the first if statement.
public void getIndexes(int num)
{
var wb = (Excel.Workbook)Globals.ThisAddIn.Application.ActiveWorkbook;
var wsEvars = wb.Sheets["Evars"];
var wsEvents = wb.Sheets["Events"];
Excel.Worksheet sheet = null; // Declared here
if (num == 0)
{
sheet = wsEvars;
// Rest of code
...
For reference you can hover over the keyword var and the tooltip tells you the type it will compile to.
If you've got a reference set to the Excel interop anyway then declare them like so:
Excel.Worksheet sheet = null;
Excel.Workbook wb = Globals.ThisAddIn.Application.ActiveWorkbook;
Excel.Worksheet wsEvars = wb.Sheets["Evars"];
Excel.Worksheet wsEvents = wb.Sheets["Events"];
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);
}
}
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
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.