How to run menu items of Excel via VSTO add-in? - c#

I am developing an Excel 2013 VSTO project. I need to execute existing menu items (e.g., File->Export operation).
How can I do it? I've tried to search for examples but didn't find any.

This is the VBA page for the ExportAsFixedFormat method of the Worksheet Object: https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.exportasfixedformat . There are all sorts of functionality to call from Excel Object Model, and that page has more info than the Interop Pages.
You can call the Excel Object Model through your VSTO usually like:
//inside a function call from your VSTO project
Excel.Worksheet ws = Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1];
string exportMessage = await SaveASPDFAsync(ws, #"C:\Test\.test.pdf", false);
//more code - if empty string, it was a good export
A method might look like this:
private async Task<string> SaveASPDFAsync(Microsoft.Office.Interop.Excel.Worksheet ws, string filepath, bool openAfterPublish)
{
return await Task.Run(() =>
{
bool originalDisplayAlerts = ws.Application.DisplayAlerts;
try {
ws.Application.DisplayAlerts = false;
ws.ExportAsFixedFormat(Type: Excel.XlFixedFormatType.xlTypePDF, Filename: filepath, OpenAfterPublish: openAfterPublish);
ws.Application.DisplayAlerts = originalDisplayAlerts;
return "";
}
catch (Exception ex)
{
return ex.ToString();
}
});
}

Related

C# VSTO for Outlook - Button that triggers a VBA Macro

I'm trying to create a custom Ribbon with a button that triggers a VBA Macro in Outlook 2010. Creation of the ribbon itself works fine but the button doesnt trigger the VBA.
I've tried these solutions: 1) How-to: Run existing Word VBA Macros from C# Ribbon Addin but it throws an error since AFAIK Outlook.Application doesn't have a Run() method, contrary to Excel or Word.
public void RunMacro(object otApp, object[] oRunArgs){
otApp.GetType().InvokeMember("Run",
System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.InvokeMethod,
null, oApp, oRunArgs);
}
public void RunChronosMacro(Office.IRibbonControl control)
{
RunMacro(oApp, new object[] { "TestMacro" })
}
2) The third answer of this question Call Outlook VBA code from c# but it also throws an exception {"Unknow name. ( RESULT : 0x80020006 (DISP_E_UNKNOWNNAME))"}. Maybe this was working in Outlook 2007 but it doesn't in my environnement. As this answer is inspired from Python code, I've also tried it in Python but without success.
public void RunChronosMacro(Office.IRibbonControl control)
{
try
{
otApp.GetType().InvokeMember("TestMacro",
System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.InvokeMethod,
null, otApp, arFunctionParameters);
System.Runtime.InteropServices.Marshal.ReleaseComObject(otApp);
otApp = null;
GC.Collect();
}
catch (Exception e)
{
MessageBox.Show(e.GetType().ToString());
}
}
I'm quite hard-stuck on this and as I'm not very familiar with C# and Offices add-ins, I don't know what to try next. Thanks in advance for any help.
Please try the code below:
using Outlook = Microsoft.Office.Interop.Outlook;
object oMissing = System.Reflection.Missing.Value;
// create outlook object
Outlook.Application otApp = new Outlook.Application();
string[] functionParameters =
{
sTo,
sCC,
sBCC,
sSubject,
sBody
};
// Run the macro
otApp.GetType().InvokeMember("YourVBA",
System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.InvokeMethod,
null, otApp, functionParameters);
For more information, Please refer the link below:
Need to run/invoke macro from c# code for Outlook Addin
How can I run a macro from a VBE add-in, without Application.Run?
Hopefully it helps you!

C#: Microsoft.Interop.Excel Refresh all not working

I have a really frustrating issue where whenever I call
RefreshAll() from Microsoft.Interop.Excel it does not refresh the data when I open the workbook in excel and I have to manually click the refresh all button within excel... I have even tried calling refresh all via vba and it still does not refresh the data... I am always prompted with:
"The PivotTable report was saved without underlying data. Use the
Refresh Data command to update the report."
Despite the fact that I already called the "Refresh all command."
public void applyMacro(string excelFile)
{
var excelApplication = new Microsoft.Office.Interop.Excel.Application { Visible = false };
var targetExcelFile = excelApplication.Workbooks.Open(excelFile);
try
{
string[] macros = addMacros(ref targetExcelFile);
for (int i = 0; i < 2; i++)
excelApplication.Run(macros[i]);
targetExcelFile.RefreshAll();
targetExcelFile.Save();
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadKey();
}
finally
{
excelApplication.Quit();
}
}
and in vba have tried adding the following before calling the refresh:
For Each ws In Worksheets
For Each qt In ws.QueryTables
qt.Refresh BackGroundQuery:=False
next qt
Next ws
Any ideas??
You can use this on your pivot table :
YourPivotTable.PivotCache().RefreshOnFileOpen = true;
YourPivotTable.SaveData = true;

Changing Footnote numbering fails for some word documents with Interop

I have the following code being used with Word 2016 installed, referencing Microsoft Word 16.0 Object Library:
private void RefreshFootnoteNumbering(FileManagement.FileManager FileManager)
{
Console.WriteLine(DateTime.Now.ToString() + " Refreshing footnotes DOCX");
// Opening and saving in word generates the required element
var Word = GetWordApp();
try
{
var DocxPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.ChangeExtension(FileManager.HtmlFileLocation, "docx"));
Console.WriteLine(DateTime.Now.ToString() + "\tOpening document");
var Doc = GetWordDoc(Word, DocxPath);
try
{
// Fails on these lines below (both cause the same exception)
Doc.Footnotes.NumberingRule = InteropWord.WdNumberingRule.wdRestartPage;
Doc.Footnotes.Location = InteropWord.WdFootnoteLocation.wdBottomOfPage;
Doc.SaveAs2(DocxPath, InteropWord.WdSaveFormat.wdFormatXMLDocument, AddToRecentFiles: false, EmbedTrueTypeFonts: true);
}
finally
{
Doc.Close();
Doc = null;
}
}
finally
{
Word.Quit();
Word = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
This works for most documents, however for some I get the following exception:
System.Runtime.InteropServices.COMException was unhandled
ErrorCode=-2146823680
HResult=-2146823680
HelpLink=wdmain11.chm#37376
Message=Value out of range
Source=Microsoft Word
StackTrace:
at Microsoft.Office.Interop.Word.Footnotes.set_NumberingRule(WdNumberingRule prop)
Other interop functions (iterating/manipulating fields, sections etc) work fine, it seems to be just altering footnotes in this way that have an issue. Altering them from within Word itself works fine.
Has anyone encountered this issue before? Any work arounds or alternatives?
I've tried recording a macro, and it gave this VBA code:
With ActiveDocument.Range(Start:=ActiveDocument.Content.Start, End:= _
ActiveDocument.Content.End).FootnoteOptions
.Location = wdBottomOfPage
.NumberingRule = wdRestartContinuous
.StartingNumber = 1
.NumberStyle = wdNoteNumberStyleArabic
.NumberingRule = wdRestartPage
.LayoutColumns = 0
End With
If I run this macro, I get the same error (value out of range, error number 4608) on the .Location line, whether I run from the debugger, or just view macros -> run.
I've also tried to translate that VBA into C# code:
var Options = Doc.Range(Doc.Content.Start, Doc.Content.End).FootnoteOptions;
Options.Location = InteropWord.WdFootnoteLocation.wdBottomOfPage;
Options.NumberingRule = InteropWord.WdNumberingRule.wdRestartPage;
However, this gives the same error.
Still not sure of the exact cause (possibly something further up in my code creating different sections); still not clear on why it worked when word recorded the macro, but not when running it.
Anyway, I managed to alter the C# code to the below, which seems to do the job and actually works!
foreach(InteropWord.Footnote FootNote in Doc.Footnotes)
{
FootNote.Reference.FootnoteOptions.NumberingRule = InteropWord.WdNumberingRule.wdRestartPage;
FootNote.Reference.FootnoteOptions.Location = InteropWord.WdFootnoteLocation.wdBottomOfPage;
}

Merging pdf with pdfsharp : exception en the pdfDocument.close()

I would like to create a function with PDFSharp in order to merge some pdf's.
Here is my code
public class PDF_Merge
{
static string [] strTabPdfFiles;
public static string SetPdfToMerge(string strPdfFilesInput)
{
strTabPdfFiles = strPdfFilesInput.Split(';');
return "O";
}
public static string MergeToPdf(string strPdfFilesOutput)
{
try
{
PdfDocument objDocumentFinal = new PdfDocument(strPdfFilesOutput);
foreach (string strDoc in strTabPdfFiles)
{
PdfDocument objDocument = PdfReader.Open(strDoc, PdfDocumentOpenMode.Import);
foreach (PdfPage page in objDocument.Pages)
{
objDocumentFinal.AddPage(page);
}
objDocument.Close();----------> Exception : File cannot be modified
}
objDocumentFinal.Close();
objDocumentFinal.Save(strPdfFilesOutput);
}
catch (Exception ex)
{
return ex.Message;
}
return "O";
}
}
My problem is that on the objDocument.Close() call, i have an exception : "The document cannot be modified".
Anyone could help me about that ?
Great thanks for this lib,
Best regards,
Nixeus
PdfDocument.Close should only be called for documents created with a filename or a stream. Close will then automatically save the contents to the PDF file. You must not call Save in this case.
With the sample code in the question, Close must not be called for objDocument because it was not modified and cannot be saved.
It's OK to call Close for objDocumentFinal to save the changes. You should not call Save for objDocumentFinal because this will only save the changes again.
A PDF file opened with PdfDocumentOpenMode.Import is for import only and cannot be modified.
Try PdfDocumentOpenMode.Modify instead.
Look at the Concatenate Documents sample:
http://www.pdfsharp.net/wiki/ConcatenateDocuments-sample.ashx
I know I am late to the party, but I encountered this issue today.
The close method is trying to save the document, and thus the requirement for .Modify. In this case you don't need objDocument.Close() at all. You can optionally (and probably should?) call objDocument.Dispose().

.RDLC Report 2008 (in VS 2010) ReportViewer appears with no report or data

Working with .RDLC 2005 in VS 2008 this technique worked very well, now in .RDLC 2008 as implemented in VS 2010 I get a blank (or no?) report.
I have made a couple of changes to accommodate .RDLC 2008 and at this time I am getting no exceptions. The present (not desired) output looks like:
I have a custom ReportController class that has a public method to ShowReport (also one to manage the exporting of reports, but that is not (yet) in play.)
From the asp.net page I invoke the controller in the property set (of Type DataSet, invoked by the page controller) like: (ReportController implements IDisposable)
try
{
using (var reportController = new ReportController(true))
{
_ReportViewer = reportController.ShowReport("DemonstrationList", value, phReportHolder);
if (_ReportViewer != null)
{
_ReportViewer.ShowRefreshButton = false;
_ReportViewer.ShowPrintButton = false;
_ReportViewer.Width = Unit.Pixel(700);// Unit.Percentage(99);
_ReportViewer.Height = Unit.Pixel(700);// Unit.Percentage(90);
}
}
lblRecordCount.InnerText = value.Tables[0].Rows.Count.ToString();
}
catch (Exception ex)
{
phReportHolder.InnerHtml = string.Format("There was an error attempting to process this report <br/><br/><div style='color:White;'>{0}</div>", ex.Message);
}
and the ShowReport method is:
public ReportViewer ShowReport(string ReportName, DataSet ds, HtmlContainerControl ReportContainer)
{
ReportContainer.Controls.Clear();
ReportViewer reportViewer = BuildReport(ReportName, ds);
ReportContainer.Controls.Add(reportViewer);
return reportViewer;
}
This allows me to tell the controller to put any 'valid' report into any htmlcontainercontrol using any provided dataset.
BuildReport takes the data and the report name and builds the report as:
private ReportViewer BuildReport(string ReportName, DataSet ds)
{
try
{
_activeDS = ds;
string ReportFileName = ResolveRDLCName(ReportName);
// ResolveRDLCName is used along with path strings
// initialized from configuration settings in the
// constructor to make this portable.
var viewer = new ReportViewer();
viewer.ProcessingMode = ProcessingMode.Local;
viewer.LocalReport.ReportPath = ReportFileName;
viewer.LocalReport.DisplayName = ReportName;
viewer.LocalReport.EnableHyperlinks = true;
AssignReportData(ds, viewer.LocalReport);
return viewer;
}
//...Exception handlers below are not invoked at this time
And 'AssignReportData' attaches the data to the report.
private static void AssignReportData(DataSet ds, LocalReport Report)
{
var listOfDatasources = Report.GetDataSourceNames();
foreach (string dsn in listOfDatasources)
{
ReportDataSource rds = new ReportDataSource(dsn,ds.Tables[dsn]);
Report.DataSources.Add(rds);
}
}
Development techniques ensure that dataTable/dataSource names stay in agreement (and if they were not I would get a specific exception, which I do not.)
I was having a similar problem which this blog post answered. Short answer is I needed to install the report viewer redistributable, and add the handler.
It seems like the report content gets rendered but is simply not visible.
Try to look at the generated HTML (DOM) with
Chrome: right-click on the report area, "Inspect Element" to explore the DOM
Internet Explorer: install the IE Developer Toolbar to explore the DOM
Maybe some CSS that has worked in the past now hides your report area.

Categories