Adding controls to Excel workbook, via VSTO, resets everything in VBA - vba

I have a VSTO Excel Addin solution which exposes certain methods to VBA through a ComVisible interface. Please refer this for a sample implementation. I am using VSTO 4 and Excel 2007/2010 for this.
Certain methods of the exposed interface insert a control (button, combo box or a Winform UserControl) when called from VBA. The method executes properly and inserts the control on the sheet as well.
public void AddButtonControl(string strControlName)
{
Microsoft.Office.Interop.Excel.Worksheet nativeWorksheet = Globals.ThisAddIn.Application.ActiveWorkbook.ActiveSheet;
Microsoft.Office.Tools.Excel.Worksheet vstoWorksheet = Globals.Factory.GetVstoObject(nativeWorksheet);
vstoWorksheet.Controls.AddButton(nativeWorksheet.Range["A1", "E5"], strControlName);
}
Now as soon as the control is inserted in the workbook, all the variables declared in VBA are reset. All code goes back to the state as if it were never executed.
My solution and an Excel workbook are shared here
Just build the solution and open the workbook SampleTest.xlsm
Click "Check string value", a message box saying "Default Text" would be displayed.
Click on button - "Add Button Dynamically". A button would be inserted at the top of the sheet. and a message saying - "Assigning text to string: Sample Text" would be displayed.
Now click "Check string value" again. It should have displayed "Sample Text" but a blank message box is displayed.
Any suggestions/workarounds are welcome.

Related

CommandBars("Text").Controls not deleted when exiting the document - VBA word add-in

I'm trying to create a add in for MS Word with VBA.
It has a "AutoExec" procedure that creates a new item in the CommandBar("Text") collection (right click menu) and a "AutoExit" that deletes this created item.
As an example, I tried the code below that create an item "How Many Pages?", which executes a macro displaying the number of pages in the active document.
This is the AutoExec Code:
Public Sub AutoExec()
Dim objcommandbutton As CommandBarButton
Call MsgBox("AutoExec")
Set objcommandbutton = Application.CommandBars("Text").Controls.Add _
(Type:=msoControlButton, Before:=1)
objcommandbutton.Caption = "How Many Pages?"
objcommandbutton.OnAction = "HowManyPages"
End Sub
This is the AutoExit Code:
Public Sub AutoExit()
Dim objcommandbutton As CommandBarControl
Call MsgBox("AutoExit")
For Each objcommandbutton In Application.CommandBars("Text").Controls
If objcommandbutton.Caption = "How Many Pages?" Then
objcommandbutton.Delete
End If
Next objcommandbutton
End Sub
This is the Main Macro Code:
Public Sub HowManyPages()
If Documents.Count > 0 Then
Call MsgBox(ActiveDocument.BuiltInDocumentProperties("Number of Pages"))
Else
Call MsgBox("No document is currently active.")
End If
End Sub
When exiting the document, the Button previously added in CommandBars("Text") collection is not deleted. I see this when I open a new blank Word document and the button remains in the right click menu.
I know that the routine is performed correctly because there is a MsgBox instruction to verify it.
This only happen with the AutoExit subroutine of a add-in, that is, loaded as a add-in: running the code in a macro with vba module works fine.
Any help?
When working with the CommandBars object model in Word it's necessary to always specify the Application.CustomizationContext.
Word can save keyboard layouts and CommandBar customizations in various places: the Normal.dotm template, the current template or the current document. The default when you create a CommandBar addition may not be the3 same as the default when trying to delete something.
Since this is an add-in, I assume you want the change for the entire Word environment (any open document). In that case, use the context NormalTemplate. Use this before any calls to CommandBar:
Application.CustomizationContext = NormalTemplate
Note: for saving a customization in the current document: = ActiveDocument; for saving in the template attached to the current document: = ActiveDocument.AttachedTemplate.
I solved my problem with a workaround:
I was trying to "add" a template (.dotm) as a add-in (in the "Templates and Add-ins" window) to use my VBA project in a new document. That's why I was using the AutoExec() and AutoExit() procedures.
But only now I figure out that just "attaching" the .dotm template to the active document (in the same "Templates and Add-ins" window, as show in the figure below) makes the functions Private Sub Document_Open() and Private Sub Document_Close() to run normally. Which solves my problem.
Even so, I think there is a certain "issue" with the AutoExit() procedure when trying to change the CommandBars itens. But that's ok for now.
enter image description here

Context-menu and keyboard shortcut return different results on the same method

I have a vba sub ("sub") in excel 2013, which opens another workbook, reads some data out of it, return this data and close the newopened workbook. It is possible to run this sub via keyboard shortcut and a entry in the context menu.
This call (the "UTILS.sub" this is) works perfectly fine:
' Add the sub-call to a new context menu entry
Call UTILS.addContextMenuEntry("Caption", 2556, "UTILS.sub")
But this call doesn't:
' Add the sub-call to a new keyboard shortcut
App.OnKey "+^{M}", "UTILS.sub"
If i call sub with the keyboard shortcut it breaks without an error. I've managed to work out the specific code line, at which it breakes via debugging:
'[...]
Application.ScreenUpdating = False
' Open the external Workbook
Set wbHandle = Workbooks.Open("wb.xls", ReadOnly:=True)
MsgBox "Debug"
'[...]
wb.xls opens (and displays), but the MsgBox "Debug" does not. Nothing after the "Open"-line does run and no breakpoint after this line will be hit. Another strange thing: If i debug a call of sub with a breakpoint before that line, it all works perfectly.
How to get the sub to run correctly, not regarding wether it was called by a context menu entry or keyboard shortcut?
I'm not exactly sure what the cause of this behavior is, but I suspect that macros called via keyboard shortcuts or context menus are actually passed through Application.Run or something else that can reset the execution state internally.
The simple work-around seems to be to pass execution through a "wrapper" sub:
Public Sub bar()
foo
End Sub
Public Sub foo()
Dim bar As Workbook
Set bar = Workbooks.Open("C:\Book1.xls", ReadOnly:=True)
MsgBox "foo"
End Sub
Launching foo by a keyboard shortcut will not display the message box, but calling bar will.
I've found the answer to my problem by myself: stackoverflow.com/questions/17409524/
Apparently, excel can't handle the "Workbooks.Open()"-method if the sub is called through a keyboard shortcut, which includes the [SHIFT]-key. Solution: Use "Workbooks.Add()" instead.

Word VBA: Sharing variables between documents

I feel like this should be obvious, I'm just not finding the answer anywhere.
I have a Word document with several Public variables declared and defined in a variety of procedures. One procedure opens another Word document, which has a form that loads on open. For the life of me I cannot get that form in the second document to use values from the original document's variables. Am I misunderstanding the nature of Public variables? Everything I've found seems to indicate that those values should be visible to the newly opened document.
So to open the second document, I'm using the code below (some of it is declared and set elsewhere, but I think this is the relevant stuff:
Public iMarker as boolean
Sub OpenDoc()
If check_ExA.Value = True Then 'a checkbox on a userform
docName = docsPath & "/" & "seconddocumentpath.docm"
iMarker = True
formAddDocs.Hide
Set addDoc = Documents.Open(docName)
End Sub
from there, the new document has a form that shows on Open, and in the initialization, I want it to be able to see if the iMarker variable is true, among some other strings, etc.
Private Sub UserForm_Initialize()
If iMarker = True Then
'do some other stuff
End If
End Sub
I tried changing that Initialize sub to public and that did nothing. It won't see that iMarker is true when the second document opens. Any suggestions?
To make a public variable of doc01 ("source doc") available to doc02 ("target doc") document, go like follows:
open the "source doc"
open the "target doc"
click Tools-> References
from Available References listbox of the References dialog box click the "Project" of the "source doc"
You can recognize it from the path appearing near the dialog box bottom
Otherwise
close Reference dialog box
go to "source doc" and rename its project (Tools->project properties, edit "project name" textbox and click "OK") with a meaningful name (say "MainProject")
go back to "target doc" project
open References dialog box and now from Available References listbox click the "MainProject" reference
Save and close target doc

When calling Excel from VB.NET app, how do I make Excel appear in front of app?

I have a VB.NET application that uses Microsoft.Office.Interop.Excel to simply open up a spreadsheet, pump over some data to it, refresh the data in the pivot tables and then present the user with the spreadsheet.
The code is pretty simple (error handling not shown):
' Start Excel Application
xlApp = New Excel.ApplicationClass
' Get Excel Workbooks
xlWorkBooks = xlApp.Workbooks
' Open workbook
xlWorkBook = xlWorkBooks.Open(Filename:=sExcelDocFullPath, IgnoreReadOnlyRecommended:=blnIgnoreReadOnly)
If Not xlWorkBook Is Nothing Then
' Pump some data over (code not shown)
' Make the first worksheet in the document active
CType(xlWorkBook.Worksheets(1), Excel.Worksheet).Activate()
'Return control of Excel to the user
xlApp.Visible = True
xlApp.UserControl = True
' Refresh workbooks (with alerts off, so as not to hassle user )
xlApp.DisplayAlerts = False
xlWorkBook.RefreshAll()
End If
What happens when this code is run, is that Excel is opened up behind the calling app and because we are refreshing the data connections, the small Excel "SQL Server Login" dialog is also displayed behind the calling app and thus the user needs to ALT+TAB to Excel or click on Excel in the Task Bar - neither is a great experience for the user. We'd like the Login dialog to appear infront of the calling app.
Can anyone suggest how this could be achieved ? I've tried moving the setting of the Visible & UserControl properties to after the RefreshAll, but that doesn't make any difference.
Cheers,
Chris.
Sorry for C#, I don't know VB, so I cannot translate myself, but the main idea is to use WinAPI:
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
You can get hwnd from xlApp:
BringWindowToTop((INtPtr)XlApp.Hwnd);

Accessing Add-In from e-mail body context menu

Using Visual Studio 2015 to build an addin for Outlook 2013. I have already built this addin for Excel 2013. The addin is to be accessed from the context menu in the body of an e-mail.
The following snippet is typically how I have added the button to the Excel context menu but can't seem to find how to do this for an outlook e-mail:
Dim contextmenu As Office.CommandBar
Dim DDHButton As Office.CommandBarButton
contextmenu = Application.CommandBars("cell")
DDHButton = contextmenu.Controls.Add(Type:=Office.MsoControlType.msoControlButton, Before:=20)
With DDHButton
.FaceId = 2308
.Caption = "Button Name"
.Tag = "New Button"
.TooltipText = "etc. etc."
End With
I have tried to alter this to the following:
contextmenu = Application.ActiveExplorer.CommandBars("ContextMenuMailItem")
And many other variations of this line to no avail.
I have also come across suggestions of creating a ribbon and editing the xml file but have also had absolutely no luck with this either!
Any suggestions?
Your context menu must come from the ribbon XML returned by your addin. See https://msdn.microsoft.com/en-us/library/office/ee692172(v=office.14).aspx