I am creating an application level add-in for Word 2010 using C# VSTO. The add-in has a Custom Task Pane with its visibility controlled by a toggle button on a ribbon. The ribbon has been created with XML (not the Visual Studio Designer).
When I call ribbon.Invalidate from outside the add-in's ribbon class I cannot refresh my ribbon because it is null. I get a System.NullReferenceException that issues the message:
Object reference not set to an instance of the object
I suspect that the ribbon is null because the underlying XML is not loaded when it is called. I have tried many things including
Invalidating Ribbon from Outside Ribbon
which suggests defining a class-level Office.IRibbonUI in the ThisAddIn class, and setting the value of this in the Ribbon_Load callback. I still received the exception with this and all other attempts I made. Here's my code:
In the myRibbon class I have:
[ComVisible(true)]
public class myRibbon : Office.IRibbonExtensibility
{
public Office.IRibbonUI ribbon;
private bool isTaskPaneVisible;
public bool IsTaskPaneVisible
{
get { return isTaskPaneVisible; }
set
{
isTaskPaneVisible = value;
// This is where the null exception is thrown
ribbon.Invalidate();
}
}
and
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
{
this.ribbon = ribbonUI;
}
In the ThisAddin class I have:
public partial class ThisAddIn
{
internal myRibbon myRibbon;
and
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
myRibbon = new myRibbon();
return myRibbon;
}
Any assistance in getting ribbon.Invalidate to function correctly when called from outside the ribbon class would be greatly appreciated.
More than two and a half years after posting this question, the solution came to me while working on a different VSTO project. The issue was with the XML for the VSTO project.
ribbon.Invalidate was null because
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
{
this.ribbon = ribbonUI;
}
assigned a null value to ribbon. And ribbonUI was null because the Ribbon XML file was missing a reference to Ribbon_Load.
So the original XML file read
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" xmlns:nsCustom="Custom Namespace">
But it should have had onLoad="Ribbon_Load" included
<customUI onLoad="Ribbon_Load" xmlns="http://schemas.microsoft.com/office/2009/07/customui" xmlns:nsCustom="Custom Namespace">
I revisited the code I wrote and confirmed that this resolved the issue.
Was the Ribbon_Load callback called before you try to use the IRibbonUi instance?
There is no need to call the Invalidate method if the Load callback is not yet called. The fact is that your callbacks will be invoked automatically for the first time right after the Load one.
Read more about the Fluent UI (aka Ribbon UI) in the following series of articles in MSDN:
Customizing the 2007 Office Fluent Ribbon for Developers (Part 1 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 2 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3)
Related
I have the purpose to create a com-visible class with UI elements which I can execute/access via Lotus Notes. This is no problem using Visual Studio 2017. My com-visible class is correctly registered in Windows GAC and I'm able to call the available/visible functions supplied by a com-visible class:
[Guid("AD2C59FF-7B27-458E-9745-CB27092BAC9E")]
[ClassInterface(ClassInterfaceType.None)]
public class LotusNotesIntegrator
{
public void OpenDocumentUI(string filePath, string label, string password)
{
try
{
SingeltonUtils.Instance.MainForm.Show();
}
catch (Exception exception)
{
throw;
}
}
public void SetFocus()
{
}
}
I get in trouble when it comes down to usability for the user. Lotus Notes always 'steals' the shortcuts when I'm inside of the UI of my C# com-visible dll which displays a form with a simple text box. As soon as I try to copy the input text of the text box using 'Crtl + C' the shortcut is executed in the underlaying Lotus Notes client. This is a strange behavior as the UI of the com-visible dll is currently with focus and I'm able to type into the textbox field right after the form.Show() method was executed. But the shortcuts are still referenced to the Lotus Notes client which is the host/origin of the com-visible dll.
Has anyone of you experience a similar or the same issue? I hope you guys have an idea or solution for this problem.
Thanks in advance for your effort!
// Martin
I have a VBA project for Microsoft Office Outlook, which I'd like to rewrite as an Outlook Add-in with the help of NetOffice.
Here's a piece of VBA code which I'd like to transfer:
Dim objNS As Outlook.NameSpace
Set objNS = Application.GetNamespace("MAPI")
Set m_colCalendarItems = objNS.GetDefaultFolder(olFolderCalendar).Items
Application represents the running Outlook application.
My respective code in NetOffice looks like this:
Outlook.Application objApp = Outlook.Application.GetActiveInstance();
Outlook._NameSpace objNS = (Outlook._NameSpace)objApp.GetNamespace("MAPI");
m_colCalendarItems = (Outlook.Items)objNS.GetDefaultFolder(OlDefaultFolders.olFolderCalendar).Items;
Quite a lot of casts, surely this can be handled better. But the main problem is that I don't get a reference to the running application in the first line (objApp is null). Although this code is in the Addin_OnStartupComplete routine.
Any tips on how to set this up better?
I found the solution. The code snippet I postet is running in a class method. It is called from the Addin_OnStartupComplete in the Addin class (Auto-generated by the NetOffice Developer Toolbox).
I can get a reference to the running application: It's the Application property of the Addin class. I can provide this to the called method:
public class Addin : Outlook.Tools.COMAddin // this was auto-generated by the NetOffice Developer Toolbox
{
FolderEvents m_folderevents = new FolderEvents(); // 'FolderEvents' is my class
// additional auto-generated code removed
private void Addin_OnStartupComplete(ref Array custom)
{
m_folderevents.InitFolders(this.Application);
}
}
OK, so I've done a lot of googling trying to find information on this topic and I've come up pretty much empty handed. Maybe I'm not searching for the correct terminology for what I'm trying to accomplish.
My issue is that I've written a function in a MS Excel add-in, I followed the instructions from Microsoft as a starting point, but their tutorial has the code execute every time the user saves the document. My goal is to have a button on the ribbon I designed execute this function rather than the save button.
This is the Microsoft article that I followed to get myself started: https://msdn.microsoft.com/en-us/library/cc668205.aspx
I also found this question on here, but it didn't have enough detail for me to figure out how to implement the solution for myself: How to connect a ribbon button to a function defined in an Excel add-in?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
namespace ExcelAddIn1
{
public partial class ThisAddIn
{
void FormatTime(Microsoft.Office.Interop.Excel.Workbook WB, bool SaveAsUi, ref bool Cancel)
{
/////MY FUNCTION BODY HERE//////
}
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
Thanks in advance for your assistance.
VSTO provides two ways for creating a custom UI:
The Ribbon designer - see Walkthrough: Creating a Custom Tab by Using the Ribbon Designer.
A raw XML markup - Walkthrough: Creating a Custom Tab by Using Ribbon XML.
In both cases you may access the add-in properties and methods using the Globals.ThisAddin property which returns an instance of the add-in class (shown in your code listed above).
Usually you can use Globals.ThisAddIn.Application to access application level and document level UI.
I hope this link can help. Here is a sample of adding a button to a worksheet like this:
Globals.Factory.GetVstoObject(
Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets[1])
.Controls.AddControl(button, selection, buttonName);
Looks like
I have successfully built a C# Word 2013 project (ReportGenerator) that opens an MS ACCESS database and generates a MS WORD 2013 report. The results are very good. The issue I have is at the moment it can only be run from inside Visual Studio. My boss wants it to run via a windows form.
I have the competence to build a new project (ReportRunner) that contains a windows form with a datagrid, populate it and put a button on it. What I lack is the competence to know how to:
Open the report generation code from ReportGenerator in the
onclick event of ReportRunner
Pass a variable from ReportRunner to ReportGenerator so to avoid
hard coding.
I was expecting to be able to write a line like “ReportGenerator.ThisDocument.ThisDocument_Startup” in the click event of the button. This isn't happening.
The significant bits of code in my projects are:
ReportGenerator
namespace ReportGenerator
{
public partial class ThisDocument
{
ReportData reportData = new ReportData();
public void ThisDocument_Startup(object sender, System.EventArgs e)
{
int idToLookFor = 2;
reportData = MyFunctionToReadAccessData(idToLookFor);
MyFunctionToPutDataIntoReport();
}
}
}
ReportRunner
using ReportGenerator;
namespace ReportRunner
{
public partial class Form1 : Form
private void button1_Click(object sender, EventArgs e)
{
int idToLookFor = int.Parse(dataGridView1.CurrentRow.Cells[0].Value.ToString());
//HOW DO I MAKE IT OPEN REPORT GENERATOR ThisDocument_Startup
// AND PASS IT THE idToLookFor
}
}
Update:
I'm having trouble understanding your comment so here's a few updates:
You can call method from a Document-level Addin from a seperate C# WinForm using the link I provided. It doesn't matter if it's an Application-level addin or a Document-level addin - the approach is the same. See this link.
Why did you build a ReportRunner Form project that is separate from your ReportGenerator Add-in project? As I said below, you can create a single VS solution with 2 projects - one is a Document-level addin, the other is a WinForm and you can simply call the WinForm from the Ribbon associated with the addin.
I assume that you're asking how to call a function from a Word Addin from a Winform? I recently explained how to do this here: How to call a VSTO AddIn method from a separate C# project?
That being said, I don't recommend doing this becaues you can simply package your WinForm together with your Addin and then open it like this using a Ribbon:
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Form1 aForm = new Form1();
aForm.Show();
In visual studio I have an Excel 2010 Add-in project. How can I have that project create the following module:
I know I can save that workbook with that module then use it with my add in. It will be nice if I can have my add-in create that module...
It is possible to create the module. However for this to work the setting to "Trust access to the VB Project model" must be selected in Excel. It throws an error that access is denied if the trust setting is not selected.
using Excel = Microsoft.Office.Interop.Excel;
using VB = Microsoft.Vbe.Interop;
Excel.Application eApp = new Excel.Application();
eApp.Visible = true;
Excel.Workbook eBook = eApp.Workbooks.Add();
VB.VBProject eVBProj = (VB.VBProject)eBook.VBProject;
VB._VBComponent vbModule = eVBProj.VBE.ActiveVBProject.VBComponents.Add(VB.vbext_ComponentType.vbext_ct_StdModule);
String functionText = "Function MyTest()\n";
functionText += "MsgBox \"Hello World\"\n";
functionText += "End Function";
vbModule.CodeModule.AddFromString(functionText);
I dont think that VSTO supports Excel UDF's, the general recommendation is to use Automation Add-in's (as Sid's link suggests).
Another option is to call a managed VSTO function from VBA. Once again this is not recommended but possible.
(Recap of tutorial from link)
Here is any easy way to call Managed functions from VBA.
Create a class with your functions in VSTO
<System.Runtime.InteropServices.ComVisible(True)> _
Public Class MyManagedFunctions
Public Function GetNumber() As Integer
Return 42
End Function
End Class
Wire up your class to VBA in VSTO
Private Sub ThisWorkbook_Open() Handles Me.Open
Me.Application.Run("RegisterCallback", New MyManagedFunctions)
End Sub
Create Hook for managed code and a wrapper for the functions in VBA
In a VBA module in your spreadsheet or document
Dim managedObject As Object
Public Sub RegisterCallback(callback As Object)
Set managedObject = callback
End Sub
Public Function GetNumberFromVSTO() As Integer
GetNumberFromVSTO = managedObject.GetNumber()
End Function
Now you can enter =GetNumberFromVSTO() in a cell, when excel starts the cell value should be 42.
http://blogs.msdn.com/b/pstubbs/archive/2004/12/31/344964.aspx
If what you really want to do is to write .NET UDFs, or a combined .NET application level command and UDF addin then using VSTO is not currently a good solution: I would recommend using either Addin Express (costs) or Excel DNA (free). Both of these allow you to create both .NET XLL UDF addins and Automation UDF addins (XLL UDF addins offer significant performance advantages but with slightly more restricted access to the Excel object model)
A VSTO addin can't create UDF's, so you need to create a separate addin for the functions. Although this addin can be in the same DLL as the VSTO addin, you cannot communicate between the VSTO and the UDF's without special trickery.
I have a blog post about this. It gives you a complete example project that includes VSTO and UDF's.
Here is the basic structure of the UDF itself.
[Guid("3B81B6B7-3AF9-454F-AADF-FAF06E5A98F2")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IFunctions
{
int MYINT();
}
[Guid("F58C591D-A22F-49AD-BC21-A086097DC26B")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class Functions : IFunctions
{
public int MYINT()
{
return 42;
}
}