Make Org Chart with interop excel - c#

I want to create an excel worksheet with an organizational chart with data from a database.
I have played around with the interop excel library in a c# console application, but i am stuck.
I can make a worksheet containing a smartart object with the hierachy chart layout(Org chart), but i dont know how to add my own data to it. When you create the chart it comes with the nodes shown in the picture below.
Is it even possible to add you own data to the chart through the interop excel library?
This is my code so far:
private static Excel.Workbook Wb = null;
private static Excel.Application Xl = null;
private static Excel.Worksheet Sheet = null;
static void Main(string[] args)
{
Xl = new Excel.Application();
Xl.Visible = true;
Wb = Xl.Workbooks.Add();
Sheet = Wb.Worksheets[1];
var myLayout = Xl.SmartArtLayouts[88];
var smartArtShape = Sheet.Shapes.AddSmartArt(myLayout, 50, 50, 200, 200);
smartArtShape.AlternativeText = "Test";
}
This is the result:

Yes, the functionality is accessible through the "interop" :-) It's just a bit... arcane, especially for those who don't come from PowerPoint.
Since a SmartArt is "wrapped" in a Shape object, it's first necessary to get the Shape, then through it the SmartArt - the Shape object has HasSmartArt (test) and SmartArt (use) properties.
(Note that I find it very useful to explicitly declare the object type, rather than rely on var to "guess". That helps immensely when trouble-shooting.)
The actual content (the "boxes") are Nodes. For some reason, the PIAs don't provide Node objects (but they are present in the COM object model), so accessing them is a bit round-about in C#. To get all the nodes, use the AllNodes property to return the collection.
The Node objects can then be addressed collection indexing nds[i]. An individual node can be assigned to a SmartArtNode or to an Office Shape object. For the purposes of this question - adding text - the next step is the same...
The "boxes" are, themselves, Office Drawing Text Boxes (Shapes) and have the TextFrame2.TextRange.Text property for accessing their content. (Note that the default content is actually a placeholder, so this property will not return anything for a newly created SmartArt.)
Note: The code in the question attempts to use AlternativeText - this is like "AltText" in HTML objects, it provides accessibilty information and is not relevant for what you're trying to do.
static void Main(string[] args)
{
excelApp = new Excel.Application();
excelApp.Visible = true;
Excel.Workbook wb = excelApp.Workbooks.Add();
Excel.Worksheet ws = (Excel.Worksheet) wb.Worksheets[1];
var myLayout = excelApp.SmartArtLayouts[88];
var smartArtShape = ws.Shapes.AddSmartArt(myLayout, 50, 50, 200, 200);
if (smartArtShape.HasSmartArt == Office.MsoTriState.msoTrue)
{
Office.SmartArt smartArt = smartArtShape.SmartArt;
Office.SmartArtNodes nds = smartArt.AllNodes;
for (int i = 1; i <= nds.Count; i++)
{
Office.SmartArtNode nd = nds[i]; //both work
var shpNode = nd.Shapes.Item(1);
nd.TextFrame2.TextRange.Text = "Testing Node " + i;
shpNode.TextFrame2.TextRange.Text = "Testing Shape " + i;
}
}
}

Related

Script C# in SSIS to manipulate an excel sheet

I'm trying to create an script C# in SSIS to create a new column in a sheet on Excel.
I need to know the IndentLevel of a cell in excel and for this i have to create a new column with this values.
I'm trying to do this (Script in c#):
Range values = sheet.get_Range("A13");
values.Value = sheet.Range["B13"].IndentLevel();
In VBA Works like this (Script in VBA inside of a excell):
Range("A16").Value = Range("B16").IndentLevel
In C# how can i do that? i'm trying everything but doenst work.
Complete script:
xlApp = new _Excel.Application();
xlApp.Visible = true;
oWB = (_Excel.Workbook)xlApp.Workbooks.Open(destFile);
_Excel.Worksheet sheet = (_Excel.Worksheet)xlApp.Worksheets[1];
sheet.Columns["B:N"].Delete();
Range values = sheet.get_Range("A13");
values.Value = sheet.Range["B13"].IndentLevel();
Getting rid of the parenthesis seems to do the task correctly.
string destFile = #"E:\StackOverflow\Sample.xlsx";
var xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.Visible = true;
var oWB = (Microsoft.Office.Interop.Excel.Workbook)xlApp.Workbooks.Open(destFile);
Microsoft.Office.Interop.Excel.Worksheet sheet = (Microsoft.Office.Interop.Excel.Worksheet)xlApp.Worksheets[1];
sheet.Range["A16"].Value = sheet.Range["B16"].IndentLevel;
The value in cell A16 is set to B16's indent level.
The only other note is to make sure that the file isn't open elsewhere, otherwise the code will open up a read-only copy.

Trying to create an excel chart in a XLS file with data

My problem currently is that the data shows up like it's suppose to in the excel file but the chart doesn't appear correctly. I am trying Method 1: http://www.dotnetperls.com/excel.
I have already looked at this: Excel Interop Apply Chart Template
Charts: (http://imgur.com/a/btgWT)
The top image is what I get the bottom image is what I want.
string tempPath = Path.GetTempPath();
//C:\appdata...\LicenseCheck
string folderPath = string.Format(#"{0}\LicenseCheck", tempPath);
//The csv file with the XMAX license usage stats
string secondPath = string.Format(#"{0}\{1}_Report_Stats_{2}_{3}.csv",
folderPath, productName, lastCheckedDate.Month, lastCheckedDate.Year);
var application = new Application();
var workbook = application.Workbooks.Open(secondPath);
var worksheet = workbook.Worksheets[1] as Worksheet;
object misValue = System.Reflection.Missing.Value;
// Add chart.
var charts = worksheet.ChartObjects() as ChartObjects;
var chartObject = charts.Add(60, 10, 300, 300) as ChartObject;
var chart = chartObject.Chart;
// Set chart range.
var range = worksheet.get_Range("A1", "C25");
chart.SetSourceData(range);
// Set chart properties.
chart.ChartType = XlChartType.xlColumnClustered;
chart.ChartWizard
(
Source: range
// Title: "graphTitle",
// CategoryTitle: "xAxis",
// ValueTitle: "yAxis"
);
chartObject.Chart.ApplyChartTemplate(#"C:\\LicenseUsageStatsTemplate.crtx");
// Save.
workbook.SaveAs("Stats.xls", XlFileFormat.xlWorkbookNormal);
workbook.Close(true, misValue, misValue);
application.Quit();
releaseObject(worksheet);
releaseObject(workbook);
releaseObject(application);
Found out that MS office had to be installed on the server to use Interop. So, ended up using EPPlus. It's an awesome library that uses OfficeOpenXML.

Extra worksheets in C# Excel app

I am writing an Excel app in C# which does some data visualization. The first stage of the analysis is to copy the data from a single sheet in an input workbook to a new sheet in an output workbook where the analysis is performed.
This runs correctly on my laptop but when the client I am producing the code for runs the same application, they get some extra worksheets created which cause the code to crash.
It's pretty difficult to debug the application on the client's PC so I am having difficultly understanding how it's possible the results are different on two different machines.
Setup code is below, any help at all would be incredibly useful. Using Microsoft.Office.Interop.Excel 14.
Excel.Workbooks wbs = excelApp.Workbooks;
Excel.Workbook dataSource = wbs.Open(inFile);
//get the data sheet from the source file
Excel.Sheets dataSourceSheets = dataSource.Worksheets;
Excel.Worksheet dataSourceSheet = (Excel.Worksheet)dataSourceSheets.get_Item(1);
//create a new workbook
dataOutput = wbs.Add();
outputSheets = dataOutput.Worksheets;
//ensure there are only 2 sheets in the output
int osc = outputSheets.Count;
for (int i = 2; i < osc; ++i)
{
outputSheets.get_Item(i).Delete();
}
//setup summary and contents pages
summaryPage = outputSheets.get_Item(1);
summaryPage.Name = "Summary";
contentsPage = outputSheets.Add(Type.Missing, summaryPage);
contentsPage.Name = "Contents";
dataSourceSheet.Copy(Type.Missing, contentsPage); //copy the sheet AFTER contents page
dataSheet = (Excel.Worksheet)outputSheets.get_Item(outputSheets.Count);
dataSheet.Name = "Raw Data";
//fails with outputSheets.Count == 5
if (outputSheets.Count != 3)
{
throw new Exception("Error, there are too many sheets here!");
}

While creating charts in hidden Word2010, Excel opens. How to disable this?

Question in short :
this line:
doc.InlineShapes.AddOLEObject("Excel.Chart.8"); // doc is a Microsoft.Office.Interop.Word.Document object
opens Excel. How to disable this?
more details:
I am trying to create docx documents from a template with scripting Word2010 in c#.
I open a document this way:
Word.Document doc = app.Documents.Open(#"xxxx.docx",Visible:false);
During the script, Word2010 does not appear, but Excel2010 does, when I create a chart inside the word document ("Excel.Chart.8")
I see the whole chart scripting process on my monitor, which is not what I want.
Is there any way to hide Excel2010 (Chart Tools) during the process?
edit: An example of creating an excel chart:
Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();
Word.Document doc = app.Documents.Open(#"xxxx.docx",Visible:false);
string classtype = "Excel.Chart.8";
Bookmark shapeBookMark = doc.Bookmarks.get_Item("mybookmark");
Word.InlineShape wrdInlineShape = doc.InlineShapes.AddOLEObject(classtype, Range: shapeBookMark.Range);
if (wrdInlineShape.OLEFormat.ProgID == classtype)
{
object verb = Word.WdOLEVerb.wdOLEVerbHide;
wrdInlineShape.OLEFormat.DoVerb(ref verb);
Excel.Workbook obook = (Excel.Workbook)wrdInlineShape.OLEFormat.Object;
Excel.Worksheet sheet = (Excel.Worksheet)obook.Worksheets["Sheet1"];
//then the access of a cell goes like this:
((Microsoft.Office.Interop.Excel.Range)sheet.Cells[1, 1]).Value = "data";
}
Notice that Visible:false parameter must be used, because otherwise both excel and word will appear on a long scripting process. On a short one, it will not appear, but I need to do long scripting processes (creating, filling and formatting 16 charts/docx)
I can recreate this on my machine, although I'm using Office 2007 rather than 2010. The only semi-solution I've managed to find so far is to get the active instance of Excel and switch it to invisible. Excel will flash up for a second and then disappear, but at least it will not remain visible while you perform your scripting/charting.
So inserting this into your code:
Word.InlineShape wrdInlineShape = doc.InlineShapes.AddOLEObject(classtype, Range: shapeBookMark.Range);
Excel.Application xlApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
xlApp.Visible = false;
if (wrdInlineShape.OLEFormat.ProgID == classtype)
{
// etc...

How do you copy an Excel worksheet into a new workbook AND bring all the charts, images, etc.?

Our application has distribution functionality. It takes several Excel 2007 spreadsheets, copies them into a single sheet, then emails them to the users. The problem is that images and charts are not copying over. I have tried everything I can find on here and various other sources, but sadly nothing seems to work. Our application is written using VSTO, and I have also tried OpenXML, but nothing seems to work. In fact trying to copy over in OpenXML corrupted the files so badly that Excel could not read or recover them. Here's the code we use (note: we have ExcelWrapper simply calls the same functions on the Excel App but hides all the optional items).
private void CreateWorkbook(string filePath, string batchReportsPath)
{
//place the xlsx file into a workbook.
//call getfilesnames
Excel.Workbook bookToCopy;
Excel.Workbook newWorkbook;
Excel.Worksheet tempSheet = new Excel.Worksheet();
newWorkbook = ExcelWrapper.WorkbooksAdd(ExcelApp.Workbooks);
if (File.Exists(filePath))
File.Delete(filePath);
ExcelWrapper.WorkbookSaveAs(newWorkbook, filePath);
List<string> filePaths = new List<string>(Directory.GetFiles(batchReportsPath));
filePaths.ForEach(delegate(string reportPath)
{
string reportPathAndName = reportPath;
bookToCopy = ExcelWrapper.WorkbooksOpen(ExcelApp.Workbooks, reportPathAndName);
int nextSheetNumber = newWorkbook.Sheets.Count;
((Excel.Worksheet)sheetToSend.Sheets[1]).Copy(Type.Missing, newWorkbook.Sheets[nextSheetNumber]);
ExcelWrapper.WorkbookClose(bookToCopy);
});
newWorkbook.Save();
ExcelWrapper.WorkbookClose(newWorkbook);
bookToCopy= null;
tempSheet = null;
newWorkbook = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
I have tried every promising option and searched both the VSTO and OpenXML object models and I am stumped. Please stackoverflow community, you're my only hope.
UPDATE: Here's the answer folks:
//Copy all the images, Charts, shapes
foreach (Excel.Shape o in copySheet.Shapes)
{
if (o.Type == Microsoft.Office.Core.MsoShapeType.msoPicture)
o.CopyPicture();
else
o.Copy();
newWorkbook.Save();
}
You need to do the save after each copy to get the Paste to finalize. Thanks for your input.
You'll need to check the WORKSHEET object of the worksheet you're looking to copy, then run through all the "*Objects" properties, and, for each of those collections, write code to manually copy all the elements in that collection to the new sheet.
For example, you've got:
ChartObjects
ListObjects
OleObjects
Shapes (Which might get copied along with the sheet, I'm not sure).
Perhaps you can get there by a copy/paste? Here's an example that copies cell data, charts and images: MSDN
Good luck!
I don't have an answer but I can give you some pointers. First, this is not an openxml question, it's a vsto question. Second, images and charts are attached to a spreadsheet, they are not content in it (a subtle distinction). There is probably a property of all charts and one of all images attached to the sheet. Copy that across.
(I use the IExtensibility interface instead of VSTO so I know what's in the underlying API, but not the property names - sorry.)
This is how I do it -- moves the entire worksheet from source workbook to a brand new workbook:
public static void MoveWorksheet()
{
public static Excel.Application oXL;
public static Excel._Worksheet ws;
if (File.Exists(Global.Variables.GlobalVariables.recap))
{
//workbookToOpen is location and name
oXL.Workbooks.Open(workbookToOpen, Missing.Value, true);
object ws = null;
try
{
ws = wb.Sheets["Sheet 1"];
if (ws != null)
{
((Worksheet)ws).UsedRange.Interior.ColorIndex = Constants.xlNone;
((Worksheet)ws).Copy();
oXL.ActiveWorkbook.SaveAs("SaveAsFileName" + ".xlsx");
oXL.ActiveWorkbook.Close(true);
}
}
catch {}
}
}

Categories

Resources