I am creating charts in PowerPoint. The below code opens two excel applications. One opens in the background that is invisible. The second one opens after the method ends. I need to make sure second excel either never open ideally or I can close it after it opens.
I have tried the below things but none worked.
I have tried forcing GC, Manual ReleaseComObject, Killing Excel process
I have tried separating excel COM objects and forcing GC
private void BtnInsert_Click(object sender, EventArgs e)
{
var Addin = Globals.ThisAddIn;
Microsoft.Office.Interop.PowerPoint.Application activeApplication = Addin.Application;
DocumentWindow activeWindows = activeApplication.ActiveWindow;
Microsoft.Office.Interop.PowerPoint.View activeView = activeWindows.View;
Slide activeSlide = activeView.Slide;
Microsoft.Office.Interop.PowerPoint.Shapes slideShape = activeSlide.Shapes;
Microsoft.Office.Interop.PowerPoint.Shape shape = slideShape.AddChart2(-1, XlChartType.xl3DBarClustered, -1, -1, -1, -1, true);
Microsoft.Office.Interop.PowerPoint.Chart chart = shape.Chart;
//Access the chart data
Microsoft.Office.Interop.PowerPoint.ChartData chartData = chart.ChartData;
chartData.Activate();
//Create instance to Excel workbook to work with chart data
Workbook workbook = chartData.Workbook;
Microsoft.Office.Interop.Excel.Application workbookApplication = workbook.Application;
workbookApplication.Visible = false;
workbookApplication.WindowState = XlWindowState.xlMinimized;
//Accessing the data worksheet for chart
Worksheet worksheet = workbook.Worksheets[1];
// I am adding data here
// This is not required to reproduce this
chartData.BreakLink();
workbook.Close(true);
}
Also, note that this issue does not occur while updating data.
Remove chartData.Activate() and chartData.BreakLink() solves this.
Although online documentation says that chartdata.activate is required before accessing the workbook.
Otherwise, we will get a null reference.
I think the documentation is incorrect or it does not apply to vsto.
Related
I'm working with Spire.XLS licenced pack and I need to find a way to set the values for a chart manually from a c# win form application, speaking about category labels to actual values to display inside, instead of being forced to use sheet.Range[], or at least be able to use single cell values separated one from another from a sheet and use those like an "extended range" for the values any idea if this is possible?
Yes, it is possible. See the following example which shows how to create a chart without using worksheet data range.
using Spire.Xls;
namespace Create_chart
{
class Program
{
static void Main(string[] args)
{
//Create a workbook
Workbook wb = new Workbook();
//Get the first worksheet
Worksheet sheet = wb.Worksheets[0];
//Add a chart to the worksheet
Chart chart = sheet.Charts.Add();
//Add a series to the chart
var series = chart.Series.Add();
//Add data
series.EnteredDirectlyValues = new object[] { 10, 20, 30 };
//Save the file
wb.SaveToFile("result.xlsx", ExcelVersion.Version2013);
}
}
}
For more information, you can check this documentation: Create Chart without Using Worksheet Data Range in C#
This image show what i basically want to do
I have plenty excel files that i need to prepare before inserting the data into a SQL database, one of the steps is unmerge excel cells and duplicate the data, i'm doing this doc parse with c#
I found a solution with VBA Macro Excel here
Sub UnMergeFill()
Dim cell As Range, joinedCells As Range
For Each cell In ThisWorkbook.ActiveSheet.UsedRange
If cell.MergeCells Then
Set joinedCells = cell.MergeArea
cell.MergeCells = False
joinedCells.Value = cell.Value
End If
Next
End Sub
But i need to do it on c# with microsoft.office.interop.excel
Does anyone know if there's a way to do this?
C# code is very similar:
private void UnMergeFill(Workbook wb)
{
foreach (Range cell in ((_Worksheet)wb.ActiveSheet).UsedRange)
{
if (cell.MergeCells)
{
var joinedCells = cell.MergeArea;
cell.MergeCells = false;
joinedCells.Value = cell.Value;
}
}
}
I am trying to write a Winform application and dock Excel inside it as a control. Everything works until I tried to run an Excel macro (written in VBA) from my c# app. When this macro tried to access ActiveWorkBook, it is Nothing.
Funny thing is, if I comment out the "SetParent(xlHwnd, this.Handle);", the ActiveWorkBook is available. Seems like the window state of Excel application is affecting the current ActiveWorkbook.
Below is my code:
xlApp = newMicrosoft.Office.Interop.Excel.Application();
xlApp.WindowState = XlWindowState.xlMinimized;
xlApp.DisplayFormulaBar = false;
xlApp.ShowWindowsInTaskbar = false;
xlApp.DisplayAlerts = false;
xlApp.DisplayStatusBar = false;
xlApp.Interactive = true;
xlApp.Visible = true;
xlHwnd = (IntPtr)xlApp.Hwnd;
//Windows API call to change the parent of the target window.
SetParent(xlHwnd, this.Handle);
//Wire up the event to keep the window sized to match the control
SetWindowPos(xlHwnd, 0, this.DisplayRectangle.Left, this.DisplayRectangle.Top, this.DisplayRectangle.Width, this.DisplayRectangle.Height, 0X0040 | 0X4);
this.SizeChanged += newEventHandler(Panel1_Resize);
xlWorkbook = xlApp.Workbooks.Add();
vardbAddIn = (AddIn)xlApp.AddIns[3];
if(dbAddIn != null)
{
WorkbookdbWorkbook = xlApp.Workbooks.Open(dbAddIn.FullName);
if(dbWorkbook != null)
{
dbWorkbook.Application.Run("myMacro1");
}
}
According to MSDN you have to use ThisWorkbook instead of ActiveWorkbook in your macro because this is the one that contains the macro.
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/application-thisworkbook-property-excel
I have this code that works fine, adding a FreezePane to row 7, col 3 of the first sheet (counting from the left):
private void FreezePane(int rowNum, int colNum)
{
Range cellToFreeze = (Range)_xlSheet.Cells[rowNum, colNum];
cellToFreeze.Activate();
cellToFreeze.Application.ActiveWindow.FreezePanes = true;
}
After adding another couple of sheets (the second sheet contains data as a source for a pivot table, and the third sheet contains the pivot table), I want to add a freezepane in the same place of the third/pivot Table sheet, and so I tried this:
private void FreezePanePivotTable(int rowToFreeze, int colToFreeze)
{
Range pivotTableCellToFreeze = (Range)_xlPivotTableSheet.Cells[rowToFreeze, colToFreeze];
pivotTableCellToFreeze.Activate();
pivotTableCellToFreeze.Application.ActiveWindow.FreezePanes = true;
}
That, though, crashed with "Activate method of Range class failed Exception Source: Microsoft Office Excel
Exception StackTrace: at System.RuntimeType.ForwardCallToInvokeMember(..."
So I thought, "maybe you can only have one frozen pane in a workbook" and tried calling only the new method (leaving the first sheet unfrozen), and I get this seemingly bizarre err msg: "Unable to set the Size property of the Font class"
Where this exception is occurring is in the second sheet - the source data for sheet 3/PivotTable sheet! Why does setting the font size all of a sudden cause a problem? Line 3418 is the last line below:
var itemCodeLabelCell = _xlPivotDataSheet.Cells[1, 1];
itemCodeLabelCell.Value2 = "ItemCode";
itemCodeLabelCell.Style.WrapText = false;
itemCodeLabelCell.Style.Font.Size = 12;
Do different rules apply to adding freezepanes on the main (first) sheet and others, or is it that FreezePanes and PivotTables can't coexist, or what?
SomeSheet.SomeRange.Activate() fails if SomeSheet is not already active, hence probably the "Activate method of Range class failed".
You must activate the sheet before:
_xlPivotTableSheet.Activate();
pivotTableCellToFreeze.Activate();
Im trying to translate this VBA code from an Outlook AddIn to C#
Private Sub objInspector_Activate() Handles objInspector.Activate
Dim wdDoc As Microsoft.Office.Interop.Word.Document = objInspector.WordEditor
wdDoc.Windows(1).Panes(1).View.Zoom.Percentage = lngZoom
End Sub
But I can't get access to the Panes.View.Zoom.Percentage property
The main idea is that when the user opens an email, he will get a custom zoom level.
What I got at the moment is:
void Inspector_Activate()
{
// this bool is true
// bool iswordMail = objInspector.IsWordMail();
//I get the word document
Document word = objInspector.WordEditor as Microsoft.Office.Interop.Word.Document;
word.Application.ActiveDocument.ActiveWindow.View.Zoom.Percentage = 150;
// at this point i'm getting an exception
// I've also tried with
// word.ActiveWindow.ActivePane.View.Zoom.Percentage = 150; getting the same exception
}
The exception is :
An exception of type 'System.Runtime.InteropServices.COMException'
occurred in OutlookAddInTest.dll but was not handled in user code
Additional information: This object model command is not available in
e-mail.
I'm quite new in C# and Office addins, any advise?
Use word.Windows.Item(1).View.Zoom.Percentage = 150 (where word comes from Inspector.WordEditor)
word.Application.ActiveDocument.ActiveWindow.View.Zoom.Percentage = 150;
What property exactly fires the exception?
Anyway, there is no need to call the Application and ActiveDocument properties in the code. The WordEditor property of the Inspector class returns an instance of the Document class (not Word Application instance).
Thanks to Eugene Astafiev for his help.
The square brackets did the trick
VBA
Private Sub objInspector_Activate() Handles objInspector.Activate
Dim wdDoc As Microsoft.Office.Interop.Word.Document = objInspector.WordEditor
wdDoc.Windows(1).Panes(1).View.Zoom.Percentage = 150
End Sub
C#
private void Inspector_Activate()
{
Document wdDoc = objInspector.WordEditor;
wdDoc.Windows[1].Panes[1].View.Zoom.Percentage = 150;
}
I've been wanting this forever, and then I stumbled on a nice project in the MSDN Gallery Outlook 2010: Developing an Inspector Wrapper. It has a set of wrappers for all the Outlook objects, so you get a true event for every item of interest. Not sure if it's the most efficient thing ever, but it seems to work.
I have trouble with my eyesight so want black everything, and zoom everything. I seem to be able to do that by overriding the Activate() method. It's all pretty new so we'll see if it survives long term.
protected virtual void Activate() {
var activeDocument = Inspector.WordEditor as Document;
if (activeDocument == null)
return;
var mailZoom = GetSetting("MailZoom", 125);
if (mailZoom != 0)
activeDocument.Windows[1].View.Zoom.Percentage = mailZoom;
if (GetSetting("MailBlack", true)) {
activeDocument.Background.Fill.ForeColor.RGB = 0;
activeDocument.Background.Fill.Visible = msoTrue;
activeDocument.Saved = true;
}
}
In this example, GetSetting is just a function that returns a setting from an INI file. you can use constants or some other storage method.
There might be a better way to get the white on black text, but this seems pretty good.