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"];
Related
I have written a class that deals with opening, writing to, and closing an Excel file.
The methods in the Excel class are being called by a different call. When I call the addDatatoExcel method the first time, everything is working, however when I call it again, I get a "System.NullReferenceException: Excel C#". VB says I am passing a null object. I know what the error is I just can not figure out how to prevent it.
public class ExcelFile
{
public static ExcelFile C1;
private string excelFilePath = "C:\\Final.xlsx";
private int rowNumber = 1; // define first row number to enter data in Excel
Excel.Application excelApp;
Excel.Workbook excelWorkbook;
Excel.Worksheet excelWorksheet;
int ExcelCounter = 0;
int checker = 0;
string str4 = "h";
public void openExcel()
{
excelApp = null;
excelApp = new Excel.Application(); // create Excel App
excelWorkbook = excelApp.Workbooks.Add();
excelWorksheet = (Excel.Worksheet)excelWorkbook.Sheets.Add();
}
public void addDataToExcel(string str4)
{
excelWorksheet.Cells[5, 1] = str4;
ExcelCounter++;
}
public void closeExcel()
{
excelWorkbook.Close();
excelApp.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelWorksheet);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelWorkbook);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelApp);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
I want to be able to add data to the first same Excel sheet that was created.
This is the method that calls the functions in the Excel class.
public void AddTextToLabel(string str)
{
ExcelFile _Excel = new ExcelFile();
if (flag == 1) // Celsius
{
//list Adding Function 1;
temp = Double.Parse(str);
x++;
list.Add(x, temp);
zedGraphControl1.Invalidate();
CreateChart(zedGraphControl1);
//Example();
if (Excelcounter == 0)
{
_Excel.openExcel();
Excelcounter++;
}
_Excel.addDataToExcel(str);
if (Excelcounter == 15)
{
_Excel.closeExcel();
}
}
}
I have checked and the variable str that is being passed to addDatatoExcel is not null. There are other parameters that change to null when the function is called the second time. Is this because I am not saving the Excel file between every call to AddDatatoExecl();
1st call to function
2nd call to function
I am new to C# so please be specific. Thanks
You are opening the Excel, only the first time:
// Each time, you call this:
ExcelFile _Excel = new ExcelFile();
...
//The first time, this is called
if (Excelcounter == 0)
{
_Excel.openExcel();
Excelcounter++;
}
Then the next time you call the AddTextToLabel function
// A new instance is created
ExcelFile _Excel = new ExcelFile();
// The excel counter is not 0 so the _Excel.openExcel() will not be called.
if (Excelcounter == 0)
Change your code to this:
// Now your excel file is an instance member
// as is your Excelcounter
ExcelFile _Excel = new ExcelFile();
public void AddTextToLabel(string str)
{
if (flag == 1) // Celcuis
{
//list Adding Function 1;
temp = Double.Parse(str);
x++;
list.Add(x, temp);
zedGraphControl1.Invalidate();
CreateChart(zedGraphControl1);
//Example();
if (Excelcounter == 0)
{
_Excel.openExcel();
Excelcounter++;
}
_Excel.addDataToExcel(str);
if (Excelcounter == 15)
{
_Excel.closeExcel();
}
}
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";
}
}
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 copying data from first sheet of different excel files to a single workbook. I already have tried it with different alternatives like npoi, spire.xls and Interop which works good, but it kills too much of my time. It would really be thankful if anyone can suggest me with a better one. Been through many forms on the web, but couldn't find.
FYI: Each of My files are more than 50 MB in size. A few being 10 MB or less.
This is one of which I have tried (Uses Spire.xls):
workbook = new Workbook();
//laod first file
workbook.LoadFromFile(names[0]);
//load the remaining files starting with second file
for (int i = 1; i < cnt; i++)
{
LoadFIle(names[i]);
//merge the loaded file immediately and than load next file
MergeData();
}
private void LoadFIle(string filePath)
{
//load other workbooks starting with 2nd workbbook
tempbook = new Workbook();
tempbook.LoadFromFile(filePath);
}
private void MergeData()
{
try
{
int c1 = workbook.ActiveSheet.LastRow, c2 = tempbook.Worksheets[0].LastRow;
//check if you have exceeded 1st sheet limit
if ((c1 + c2) <= 1048575)
{
//import the second workbook's worksheet into the first workbook using a datatable
//load 1st sheet of tempbook into sheet
Worksheet sheet = tempbook.Worksheets[0];
//copy data from sheet into a datatable
DataTable dataTable = sheet.ExportDataTable();
//load sheet1
Worksheet sheet1 = workbook.Worksheets[workbook.ActiveSheetIndex];
sheet1.InsertDataTable(dataTable, false, sheet1.LastRow + 1, 1);
}
else if ((c1 >= 1048575 && c2 >= 1048575) || c1 >= 1048575 || c2 >= 1048575 || (c1 + c2) >= 1048575)
{
workbook.Worksheets.AddCopy(tempbook.Worksheets[0]);
indx = workbook.ActiveSheet.Index;
workbook.ActiveSheetIndex = ++indx;
}
else
{
//import the second workbook's worksheet into the first workbook using a datatable
//load 1st sheet of tempbook into sheet
Worksheet sheet = tempbook.Worksheets[0];
//copy data from sheet into a datatable
DataTable dataTable = sheet.ExportDataTable();
//load sheet1
Worksheet sheet1 = workbook.Worksheets[workbook.ActiveSheetIndex];
sheet1.InsertDataTable(dataTable, false, sheet1.LastRow + 1, 1);
}
}
catch (IndexOutOfRangeException)
{
}
}
}
Well, this works good but as said takes a long time. Any suggestions are welcome. Thanks in advance.
Here is my (fastest I know of) implementation using Excel interop. Although I looked carefully to release all (must have missed one), 2 Excel instances remain in the processes list, they are closed after the program ends.
The key is to only have 2 Open Excel instances and to copy the data as a Block using Range.Value2.
//Helper function to cleanup
public void ReleaseObject(object obj)
{
if (obj != null && Marshal.IsComObject(obj))
{
Marshal.ReleaseComObject(obj);
}
}
public void CopyIntoOne(List<string> pSourceFiles, string pDestinationFile)
{
var sourceExcelApp = new Microsoft.Office.Interop.Excel.Application();
var destinationExcelApp = new Microsoft.Office.Interop.Excel.Application();
// TODO: Check if it exists
destinationExcelApp.Workbooks.Open(pDestinationFile);
// for debug
//destinationExcelApp.Visible = true;
//sourceExcelApp.Visible = true;
int i = 0;
var sheets = destinationExcelApp.ActiveWorkbook.Sheets;
var lastsheet = destinationExcelApp.ActiveWorkbook.Sheets[sheets.Count];
ReleaseObject(sheets);
foreach (var srcFile in pSourceFiles)
{
sourceExcelApp.Workbooks.Open(srcFile);
// get extends
var lastRow = sourceExcelApp.ActiveSheet.Cells.Find("*", System.Reflection.Missing.Value,
System.Reflection.Missing.Value, System.Reflection.Missing.Value, XlSearchOrder.xlByRows,
XlSearchDirection.xlPrevious, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
var lastCol = sourceExcelApp.ActiveSheet.Cells.Find("*", System.Reflection.Missing.Value, System.Reflection.Missing.Value,
System.Reflection.Missing.Value, XlSearchOrder.xlByColumns, XlSearchDirection.xlPrevious, false,
System.Reflection.Missing.Value, System.Reflection.Missing.Value);
var startCell = (Range) sourceExcelApp.ActiveWorkbook.ActiveSheet.Cells[1, 1];
var endCell = (Range) sourceExcelApp.ActiveWorkbook.ActiveSheet.Cells[lastRow.Row, lastCol.Column];
var myRange = sourceExcelApp.ActiveWorkbook.ActiveSheet.Range[startCell, endCell];
// copy the values
var value = myRange.Value2;
// create sheet in new Workbook at the end
Worksheet newSheet = destinationExcelApp.ActiveWorkbook.Sheets.Add(After: lastsheet);
ReleaseObject(lastsheet);
lastsheet = newSheet;
//its even faster when adding it at the front
//Worksheet newSheet = destinationExcelApp.ActiveWorkbook.Sheets.Add();
// change that to a good name
newSheet.Name = ++i + "";
var dstStartCell = (Range) destinationExcelApp.ActiveWorkbook.ActiveSheet.Cells[1, 1];
var dstEndCell = (Range) destinationExcelApp.ActiveWorkbook.ActiveSheet.Cells[lastRow.Row, lastCol.Column];
var dstRange = destinationExcelApp.ActiveWorkbook.ActiveSheet.Range[dstStartCell, dstEndCell];
// this is the actual paste
dstRange.Value2 = value;
//cleanup
ReleaseObject(startCell);
ReleaseObject(endCell);
ReleaseObject(myRange);
ReleaseObject(value);// cannot hurt, but not necessary since its a simple array
ReleaseObject(dstStartCell);
ReleaseObject(dstEndCell);
ReleaseObject(dstRange);
ReleaseObject(newSheet);
ReleaseObject(lastRow);
ReleaseObject(lastCol);
sourceExcelApp.ActiveWorkbook.Close(false);
}
ReleaseObject(lastsheet);
sourceExcelApp.Quit();
ReleaseObject(sourceExcelApp);
destinationExcelApp.ActiveWorkbook.Save();
destinationExcelApp.Quit();
ReleaseObject(destinationExcelApp);
destinationExcelApp = null;
sourceExcelApp = null;
}
I have tested it on small excel files and are curious how it behaves with larger files.
I want to check if the sheet exists before creating it.
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application excel = new Excel.Application();
excel.Visible = true;
Excel.Workbook wb = excel.Workbooks.Open(#"C:\"Example".xlsx");
Excel.Worksheet sh = wb.Sheets.Add();
int count = wb.Sheets.Count;
sh.Name = "Example";
sh.Cells[1, "A"].Value2 = "Example";
sh.Cells[1, "B"].Value2 = "Example"
wb.Close(true);
excel.Quit();
This extension method returns the worksheet if it exists, null otherwise:
public static class WorkbookExtensions
{
public static Excel.Worksheet GetWorksheetByName(this Excel.Workbook workbook, string name)
{
return workbook.Worksheets.OfType<Excel.Worksheet>().FirstOrDefault(ws => ws.Name == name);
}
}
The linq method .Any() can be used instead of FirstOrDefault to check whether the worksheet exists as well...
Create a loop like this:
// Keeping track
bool found = false;
// Loop through all worksheets in the workbook
foreach(Excel.Worksheet sheet in wb.Sheets)
{
// Check the name of the current sheet
if (sheet.Name == "Example")
{
found = true;
break; // Exit the loop now
}
}
if (found)
{
// Reference it by name
Worksheet mySheet = wb.Sheets["Example"];
}
else
{
// Create it
}
I'm not into Office Interop very much, but come to think of it, you could also try the following, much shorter way:
Worksheet mySheet;
mySheet = wb.Sheets["NameImLookingFor"];
if (mySheet == null)
// Create a new sheet
But I'm not sure if that would simply return null without throwing an exception; you would have to try the second method for yourself.
Why not just do this:
try {
Excel.Worksheet wks = wkb.Worksheets["Example"];
} catch (System.Runtime.InteropServices.COMException) {
// Create the worksheet
}
wks.Select();
The other way avoids throwing and catching exceptions, and certainly it's a legitimate answer, but I found this and wanted to put this up as an alternative.