There is a problem. I have the vsto Excel template (c#). This one works perfectly, but if with it works other file excel then happens something mysterious. After exit from add-in i cant enter to it again until ill close other Excel files. I thought that it was depended on wrong way to exit from add-in, but i tryed the following ways which didn`t decide my problem:
private void ThisWorkbook_Shutdown(object sender, System.EventArgs e)
{
Application.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
I believe you have created a document-level customization. This means you will see your buttons/code running just for the one workbook.
If you really wants to have your Ribbon available just for some types of your workbooks then create a workbook template xltx. (not xlsx, xlsb or xlsm)
All workbooks created from your template will have your custom functionality. Please check this guide from Microsoft - Get started programming document-level customizations for Excel
The concept is like this.
You create a VSTO Template customization in Visual Studio
Make your Ribbon, write your code and build your project
In your Release folder you will get your Excel template, like ExcelTemplate.xltx
Distribute this file to your clients
Every time someone creates a new workbook from your template, it will have your customization but standard Excel files will not
To be honest with you I think that over years I've created just one project like this (usually I do VSTO add-ins). I don't know how you will manage updates/bug-fixes while you or your clients may already create thousands of files based on one version of your template => think in advance to have some update logic/versioning system.
I seem to remember that the one project I did, had some issue with running the code on non developer machine. I'd say I had to manually tweak registry to get it running but it may change it was really 5-6 years ago (maybe even more)
You can also consider the VSTO add-in and set the visibility (Ribbon callbacks) of your Ribbon based on some document property etc. So you will have your add-in that will load every time Excel loads but based on some internal checks it will make the Ribbon visible or hidden.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.WorkbookOpen += Application_WorkbookOpen;
Globals.ThisAddIn.Application.WorkbookActivate += Application_WorkbookActivate;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.WorkbookOpen -= Application_WorkbookOpen;
Globals.ThisAddIn.Application.WorkbookActivate -= Application_WorkbookActivate;
}
private void Application_WorkbookActivate(Excel.Workbook Wb)
{
var passThisValueToRibbonCallback = IsSupportedDocument(Wb);
}
private void Application_WorkbookOpen(Excel.Workbook Wb)
{
var passThisValueToRibbonCallback = IsSupportedDocument(Wb);
}
/// <summary>
/// An example how to check if opened/activated document is 'your' document
/// </summary>
/// <param name="workbook"></param>
/// <returns></returns>
private bool IsSupportedDocument(Excel.Workbook workbook)
{
var props = workbook.CustomDocumentProperties;
try
{
var myCustomProperty = props.Item["myPropertyThatWillBeJustInMyWorkbooks"];
return true;
}
catch (Exception)
{
return false;
}
}
Please also check this Features available by Office application and project type
Related
I am developing MS-Word Add-In using Visual Studio 2019 (with C#). I am trying to make my add-in change some Word options, like this:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
// Set numeral to "Hindi"
Application.Options.ArabicNumeral = WdArabicNumeral.wdNumeralHindi;
}
Although it works, it is not efficient because this code will be executed every time the Word application is opened. I need to execute it only once when my add-in is being installed, so I tried to create an Installer Class and do it like this:
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
Word.Application app_ = new Word.Application();
// Set numeral to "Hindi"
app_.Options.ArabicNumeral = WdArabicNumeral.wdNumeralHindi;
}
However, the second method doesn't change numeral to "Hindi" .. Any help ?
Note: To make sure that the Install function gets executed, I tried to divide by zero to make sure it will give runtime error.
I always do run StyleCop of menu or build the project when I have used StyleCop.
I want to run StyleCop when the program has been saved.
Is it possible?
Unfortunately macros have been dropped but to write an add-in is pretty easy. First of all create a new C# Add-In project (when finished you'll need to deploy your DLL into Visual Studio AddIns folder and restart VS).
Edit generated template to attach to DocumentSaved event:
private DocumentEvents _documentEvents;
public void OnConnection(object application,
ext_ConnectMode connectMode,
object addInInst,
ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
_documentEvents = _applicationObject.Events.DocumentEvents;
_documentEvents.DocumentSaved += DocumentEvents_DocumentSaved;
}
public void OnDisconnection(ext_DisconnectMode disconnectMode,
ref Array custom)
{
_documentEvents.DocumentSaved -= DocumentEvents_DocumentSaved;
}
Your DocumentEvents_DocumentSaved method will just need to invoke right VS command (please note command name varies with Visual Studio version you're using).
private void DocumentEvents_DocumentSaved(Document Document)
{
document.DTE.ExecuteCommand("Build.RunCodeAnalysisonSelection", "");
}
In this case you'll run Code Analysis only on current project (assuming it's what you saved then it's also what you want to test). This assumption fails for Save All so you may need to use "Build.RunCodeAnalysisonSolution". Of course there is a lot of space for improvements (for example when multiple near sequential saves occur).
If you're targeting VS 2013 then you shouldn't use AddIns because they have been deprecated in favor of Packages. You have same thing to do but you have that notification through IVsRunningDocTableEvents. Override Initialize() in your Package (that will implement IVsRunningDocTableEvents interface). Call AdviseRunningDocTableEvents() from IVsRunningDocumentTable (obtain with GetService()) and you're done.
Finally note that same technique applies also for any other event (after successful build, before deployment, when closing solution, and so on).
I made VSAutoBuild in reference to answer of #Adriano Repetti.
It has been published in the following URL:
https://visualstudiogallery.msdn.microsoft.com/f0930864-0637-4fb3-a34a-155375aa85b3
and github URL:
https://github.com/ko2ic/VSAutoBuild
I have an Outlook addin where I need to display a ribbon in the main Outlook window and in the Mail Read window as well. To do this I have added two ribbon xml files with the right markups in them. I then added a C# class that implements the Office.IRibbonExtensibility interface where I have implemented the GetCustomUI method which returns the right XML. Finally I did this in the ThisAddIn.cs class
protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
try
{
_ribbon = new Ribbon();
return _ribbon;
}
catch (Exception e)
{
}
return null;
}
So far so good. The ribbons load and everything shows in correct place.
Now the problem is that this Ribbon.cs file is getting rather huge as all the callbacks live in this file. Is there a way to split the callbacks into multiple classes? So if I have a Ribbon1.xml and RIbbon2.xml can I have equivalent Ribbon1.cs and Ribbon2.cs?
OK so as it turns out this is really not possible in the VSTO model. You can really only have one class which must have all the event handlers in it. The recommended approach is to use partial classes and split the code between multiple code files.
I've been experiemnting with the community version of ILNumerics 3.2.1.0 with .Net 4.0 in Visual Studio 2010 pro on Windows 7, and going through the documentation I succesfully get a windows form project to display a chart, using the code below.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void ilPanel1_Load(object sender, EventArgs e)
{
ILSurface mySurface = new ILSurface(ILSpecialData.sincf(100, 200));
ILPlotCube myCube = new ILPlotCube(twoDMode: false);
myCube.Add(mySurface);
ilPanel1.Scene.Add(myCube);
}
}
If I try exactly the same code but from inside a VSTO Excel 2010 application all that is displayed in the form is the designer view of the ILPanel, blue circle on white background. I don't get any error messages. Am I missing something obvious? or does anyone have a solution of how to get the chart to display in VSTO?
Update
Thanks to Philliproso for pointing out the IsDesignMode() method. As pointed out in various places, including this question, Detecting design mode from a Control's constructor , the following method is not ideal, but for me is has provided a quick fix to allow me to evaluate ILNumerics.
public static bool IsDesignMode() {
if (System.Windows.Forms.Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1)
{
return true;
}
return false;
}
This is the same issue as here:
Ilnumerics Ilpanel not activating when compiled in a winform into a dll when loaded into matlab
in-a-winform-into-a-dll-when-loa
Using VSTO as host for ILNumerics lets the panels assume, it was loaded in a designer. We are currently collecting possible workarounds and solutions. One solution might be to introduce a flag in the Settings of ILNumerics:
Hosted [default: false]
Your situation would require the flag to be enabled. In hosted mode, a blacklist of common designers could be checked at runtime and compared to the current entry assembly. Any other suggestions?
I am creating a VSTO Ribbon AddIn for excel, and I am storing some workbook state information in my application that I use to update visual Buttons Enabled. Considering there can be multiple workbooks I am storing this state object in a dictionary in the ThisAddIn class. My problem is that I don't know how to get a unique Hash/Key/Guid for the workbook because all I get is a COM wrapper that continually changes the hash. fair enough, I totally understand that.
One solution I've used for a long time has been to create a guid and store it in the CustomDocumentProperties for the workbook, and to map the state based on that as a key. This at least works, but it fails if I create a copy of the workbook and open that in the same Application instance and have multiple workbooks with the same guid now..
I just had an idea now that I suppose I could refresh this Guid on the Workbook_Open event. But still this seems like a dodgy solution.
The second solution I found here:
http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/04efa74d-83bd-434d-ab07-36742fd8410e/
So I used that guys code and created this:
public static class WorkbookExtensions
{
public static IntPtr GetHashery(this msExcel.Workbook workbook)
{
IntPtr punk = IntPtr.Zero;
try
{
punk = Marshal.GetIUnknownForObject(workbook);
return punk;
}
finally
{
//Release to decrease ref count
Marshal.Release(punk);
}
}
}
It works very well for a few minutes, until it starts giving me the infamous error "COM object that has been separated from its underlying RCW cannot be used" when accessing the Application.ActiveWorkbook.
Is this a safe way of referencing the Workbook COM object?
What if I had two ribbon applications both using this method to get a single workbooks GUID?
What if one of those applications runs the garbage collector on my state object, which calls a finalizer to call Marshal.FinalReleaseComObject(workbook)?
Is there any way I can get the Ref Count for a Workbook so that I don't call FinalRelease before other Ribbon Apps have finished with them?
What are some best practices for cleaning up Workbook COM objects in VSTO to keep playing fair with these other apps?
Surely I'm not the first person to want to have buttons enabled based on Workbook state, how does everyone else do this? I've looked at a few other articles here on Stack Overflow but none quite help me with the Workbook Guid solution.
I am using the Ribbon Designer, and hooking up to the Workbook Load and Deactivate events.
Thanks in advance, hope ive included all the details.
I ended up solving this by simply casting the IntPtr to an long, and then the disposal of the IntPtr doesn't affect me. I don't need to preserve the IntPtr because all I really need is something unique about the workbook.
The following code allows me to store Workbook-specific state information, so I can update the visual state of the buttons in my ribbon based on a custom object workbook state. You can store any information you'd like in your custom WorkbookState class, but typically it would be session-specific information you don't want to persist in the spreadsheet itself.
Separate Workbook extensions:
public static class WorkbookExtensions
{
public static long GetHashery(this msExcel.Workbook workbook)
{
if (workbook == null)
{
throw new ArgumentNullException("workbook");
}
IntPtr pUnknown = IntPtr.Zero;
try
{
pUnknown = Marshal.GetIUnknownForObject(workbook);
return pUnknown.ToInt64();
}
finally
{
// GetIUnknownForObject causes AddRef.
if (pUnknown != IntPtr.Zero)
{
Marshal.Release(pUnknown);
}
}
}
}
Then in my VSTO/ExcelDna ThisAddIn class I store a map of all workbook states with the above method to find a unique workbook hash key:
private Dictionary<long, WorkbookState> _workbookStates = new Dictionary<long, WorkbookState>();
public WorkbookState WorkbookState
{
get
{
long hash = Application.ActiveWorkbook.GetHashery();
WorkbookState state;
if (!_workbookStates.TryGetValue(hash, out state))
{
state = _workbookStates[hash] = new WorkbookState();
}
return state;
}
}
And of course now I can access my WorkbookState from anywhere in my ribbon application by simply calling ThisAddIn.WorkbookState
You can also use workbook.Application.Hwnd
But this would only help you if you're using Excel 2013/2016, which opens new window for each workbook.
Excel 2010 opens all workbooks under the same window.