Accessing custom task pane in active window - Visual Basic, VSTO - vb.net

I'm creating a COM add-in in VSTO for Ppt 2013 and am having a problem referencing the custom task pane in the active window.
My code is supposed to make the custom task pane visible for the active window only, however it currently runs for all document windows.
My code is:
For Each CTP As Microsoft.Office.Tools.CustomTaskPane In Globals.ThisAddIn.CustomTaskPanes
If CTP.Window Is Globals.ThisAddIn.Application.ActiveWindow Then
CTP.Visible = True
End If
Next
The taskpane is added to each new presentation created/ opened using the below code
AddIn_control1 = New AddIn_control
AddIn_taskpane = Me.CustomTaskPanes.add(AddIn_control1, "Add-in taskpane", Me.Application.ActiveWindow)

I conducted a little experiment and turns out CustomTaskPane.Window is always ActiveWindow. So to workaround it you can keep tracking of tackpanes in some dictionary:
Dictionary<CustomTaskPane, PowerPoint.Presentation> ctpDict = new Dictionary<CustomTaskPane, PowerPoint.Presentation>();
void Application_AfterNewPresentation(PowerPoint.Presentation Pres) {
AddIn_control AddIn_control1 = new AddIn_control();
CustomTaskPane AddIn_taskpane = this.CustomTaskPanes.Add(AddIn_control1, "Add-In Taskpane", this.Application.ActiveWindow);
ctpDict.Add(AddIn_taskpane, Pres);
}
and later you can use it:
if (cptDict[CTP] == Globals.ThisAddIn.Application.ActivePresentation) {
CTP.Visible = true;
}

Related

Can't Print Specific Word Document Using VB.Net [duplicate]

I have a C# WinForm application that opens and fills out a MS Word dotx template by placing text at bookmarks, and then attempts to print it, all using MS Word Interop 15.
Everything seems to go fine, the print dialog shows and completes OK, the print job shows up in the print queue (i.e. the "See what's printing" window from "Devices and Printers" on MS Windows 10). But then the job immediately disappears from the queue before it can be spooled! (document appears very very briefly with "Spooling" status, and does not print - the printer never gets the job)
Here is my code (exception checking removed for brevity):
using Word = Microsoft.Office.Interop.Word;
private void Print_Click(object sender, EventArgs e)
{
// Open the MS Word application via Office Interop
Word.Application wordApp = new Word.Application();
Word.Document wordDoc;
// Open the template
wordDoc = wordApp.Documents.Add(Template: ContractTemplatePath, Visible: false);
// Ensure the opened document is the currently active one
wordDoc.Activate();
// Set the text for each bookmark from the corresponding data in the GUI
SetBookmarkText(wordDoc, "Foo", fooTextBox.Text);
// ... There's a whole bunch of these ... then:
// Instantiate and configure the PrintDialog
var pd = new PrintDialog()
{
UseEXDialog = true,
AllowSomePages = false,
AllowSelection = false,
AllowCurrentPage = false,
AllowPrintToFile = false
};
// Check the response from the PrintDialog
if (pd.ShowDialog(this) == DialogResult.OK)
{
// Print the document
wordApp.ActivePrinter = pd.PrinterSettings.PrinterName;
wordDoc.PrintOut(Copies: pd.PrinterSettings.Copies);
}
// Close the document without saving the changes (once the
// document is printed we don't need it anymore). Then close
// the MS Word application.
wordDoc.Close(SaveChanges: false);
wordApp.Quit(SaveChanges: false);
}
The only thing I can think of here is that maybe because I do away with the document as soon as I've sent it to the printer, then the job hasn't been completely sent so it removes itself or something. If this is the case then how can I determine how long I need to keep the document around for and what's the best way of waiting for that?
EDIT: Ive done another small bit of research (dont have time for more on this just at this moment) that suggests I may be able to use the PrintEnd event, but I couldn't immediately see if this would be applicable when using Interop. Would it be a method of achieving what I want without polling?
One solution is to poll the BackgroundPrintingStatus property of the Word application. It holds a count of the documents still waiting in the printing queue. While this count is greater than 0 there are still documents awaiting printing.
There are many ways you could achieve this. Here's a simple loop which blocks the UI:
// Send document to printing queue here...
while (wordApp.BackgroundPrintingStatus > 0)
{
// Thread.Sleep(500);
}
// Printing finished, continue with logic
Alternatively you may want to wrap it in a task so that you can do other things while waiting:
await Task.Run(async () => { while (wordApp.BackgroundPrintingStatus > 0)
{ await Task.Delay(500); } });

Cannot enable a Ribbon button programmatically

I developed a VSTO 4 add-in for Excel. It works perfect, however, I have a button placed in the custom tab of its Ribbon control that is initially disabled.
After clicked other ribbon button in my custom tab, I need to enable the initially disabled button.
I tried with:
btnCancelar.Visible = true;
In the Click event of a button, but button is not shown. The strange thing is that when debugging, it still does not appear, but if a MessageBox is shown, the button get visible at last.
I don't understand this behaviour. How can I enable or disable a ribbon button dynamically by code?
I'm not sure what your language is used in your project, but I guess you can tranform it to your own language used. I'll show the example here in C#:
First you need to implement a so called Callback function in the RibbonXML definition:
<button id="buttonSomething" label="Content" size="large" getVisible="EnableControl"/>
then the next step is to implement the Callback function:
public bool EnableControl(IRibbonControl control)
{
return true; // visible ... false = invisible
}
VSTO will trigger the getVisible Callback and depending on the return value enable or disable the visible state (don't forget to remove any Visible property from the RibbonXML, otherwise the Callback is not triggered)
In case of the Ribbon Designer you need to make sure your Click signature is correct, the easies way to do that is by double clicking the button on the ribbon designer. This will create the Click method for you, for instance:
I created a Ribbon with the Ribbon designer and added two buttons. Double clicked the first button to get an empty method like below, and added the code.
private void button1_Click(object sender, RibbonControlEventArgs e)
{
// Toggle button visibility and make sure the button is enabled
// Visible (obviously) makes it visible, while Enabled is grayed if
// false. You don't need this it is Enabled by default, so just for
// demo purposes
button2.Visible = !button2.Visible;
button2.Enabled = button2.Visible;
// Force Ribbon Invalidate ...
this.RibbonUI.Invalidate();
// Long running proces
}
This worked perfectly for me, so if it doesn't work for you please provide more details of your coding.
I have created a workaround to this.
It was simple. Just started the long running process in different thread. That way, cancel button is shown when it should and then hidden after the process ends.
I used this code to launch the process in the Ribbon.cs code:
btnCancelar.Visible = true;
Action action = () => {
Formatter.GenerateNewSheet(Formatter.TargetType.ImpresionEtiquetas, frm.CustomerID, workbook, btnCancelar);
};
System.Threading.Tasks.Task.Factory.StartNew(action);
And inside the process method I have this code:
public static bool GenerateNewSheet(TargetType type, string customerID, Excel.Workbook workbook, Microsoft.Office.Tools.Ribbon.RibbonButton btnCancelar)
{
try
{
_cancelled = false;
InfoLog.ClearLog();
switch (type)
{
case TargetType.ImpresionEtiquetas:
return GenerateTagPrinting(customerID, workbook);
}
return false;
}
finally
{
btnCancelar.Visible = false;
}
}
The interesting thing here I have discovered is that Excel is thread safe, so it was not necessary to add a synchronization mechanism neither when adding rows in the new sheet nor when setting Visible property to false again.
Regards
Jaime

Open Method of Worksheet fails [duplicate]

I am trying to create a new instance of Excel using VBA using:
Set XlApp = New Excel.Application
The problem is that this new instance of Excel doesn't load all the addins that load when I open Excel normally...Is there anything in the Excel Application object for loading in all the user-specified addins?
I'm not trying to load a specific add-in, but rather make the new Excel application behave as though the user opened it themself, so I'm really looking for a list of all the user-selected add-ins that usually load when opening Excel.
I looked into this problem again, and the Application.Addins collection seems to have all the addins listed in the Tools->Addins menu, with a boolean value stating whether or not an addin is installed. So what seems to work for me now is to loop through all addins and if .Installed = true then I set .Installed to False and back to True, and that seems to properly load my addins.
Function ReloadXLAddins(TheXLApp As Excel.Application) As Boolean
Dim CurrAddin As Excel.AddIn
For Each CurrAddin In TheXLApp.AddIns
If CurrAddin.Installed Then
CurrAddin.Installed = False
CurrAddin.Installed = True
End If
Next CurrAddin
End Function
Using CreateObject("Excel.Application") would have the same result as using New Excel.Application, unfortunately.
You will have to load the Addins that you need individually by file path & name using the Application.Addins.Add(string fileName) method.
I'm leaving this answer here for anyone else who ran into this problem, but using JavaScript.
A little background... In my company we have a 3rd party web app that used JavaScript to launch Excel and generate a spreadsheet on the fly. We also have an Excel add-in that overrides the behavior of the Save button. The add-in gives you the option of saving the file locally or in our online document management system.
After we upgraded to Windows 7 and Office 2010, we noticed a problem with our spreadsheet-generating web app. When JavaScript generated a spreadsheet in Excel, suddenly the Save button no longer worked. You would click save and nothing happened.
Using the other answers here I was able to construct a solution in JavaScript. Essentially we would create the Excel Application object in memory, then reload a specific add-in to get our save button behavior back. Here's a simplified version of our fix:
function GenerateSpreadsheet()
{
var ExcelApp = getExcel();
if (ExcelApp == null){ return; }
reloadAddIn(ExcelApp);
ExcelApp.WorkBooks.Add;
ExcelApp.Visible = true;
sheet = ExcelApp.ActiveSheet;
var now = new Date();
ExcelApp.Cells(1,1).value = 'This is an auto-generated spreadsheet, created using Javascript and ActiveX in Internet Explorer';
ExcelApp.ActiveSheet.Columns("A:IV").EntireColumn.AutoFit;
ExcelApp.ActiveSheet.Rows("1:65536").EntireRow.AutoFit;
ExcelApp.ActiveSheet.Range("A1").Select;
ExcelApp = null;
}
function getExcel() {
try {
return new ActiveXObject("Excel.Application");
} catch(e) {
alert("Unable to open Excel. Please check your security settings.");
return null;
}
}
function reloadAddIn(ExcelApp) {
// Fixes problem with save button not working in Excel,
// by reloading the add-in responsible for the custom save button behavior
try {
ExcelApp.AddIns2.Item("AddInName").Installed = false;
ExcelApp.AddIns2.Item("AddInName").Installed = true;
} catch (e) { }
}

VB.NET VSTO referencing custom task panes from ribbon in multiple windows PowerPoint 2013 [duplicate]

I'm creating a COM add-in in VSTO for Ppt 2013 and am having a problem referencing the custom task pane in the active window.
My code is supposed to make the custom task pane visible for the active window only, however it currently runs for all document windows.
My code is:
For Each CTP As Microsoft.Office.Tools.CustomTaskPane In Globals.ThisAddIn.CustomTaskPanes
If CTP.Window Is Globals.ThisAddIn.Application.ActiveWindow Then
CTP.Visible = True
End If
Next
The taskpane is added to each new presentation created/ opened using the below code
AddIn_control1 = New AddIn_control
AddIn_taskpane = Me.CustomTaskPanes.add(AddIn_control1, "Add-in taskpane", Me.Application.ActiveWindow)
I conducted a little experiment and turns out CustomTaskPane.Window is always ActiveWindow. So to workaround it you can keep tracking of tackpanes in some dictionary:
Dictionary<CustomTaskPane, PowerPoint.Presentation> ctpDict = new Dictionary<CustomTaskPane, PowerPoint.Presentation>();
void Application_AfterNewPresentation(PowerPoint.Presentation Pres) {
AddIn_control AddIn_control1 = new AddIn_control();
CustomTaskPane AddIn_taskpane = this.CustomTaskPanes.Add(AddIn_control1, "Add-In Taskpane", this.Application.ActiveWindow);
ctpDict.Add(AddIn_taskpane, Pres);
}
and later you can use it:
if (cptDict[CTP] == Globals.ThisAddIn.Application.ActivePresentation) {
CTP.Visible = true;
}

Microsoft Visual Studio 2012 using Visual Basic, how to correctly write in code?

Currently I'm new to this and I'm attempting to get this all to work, right now I've looked into and made a module which allows to me to change the user-agent of the internal web-browser so I can manipulate it easily however this is where the issue lies.
I'm assuming I must be editing the auto generated code because the file itself is called "Form1.Designer.vb" and I have no idea where to start putting in custom code to override the current code such as I wanting to do something like this;
Me.WebSiteBrowser1.Dock = System.Windows.Forms.DockStyle.Fill
Me.WebSiteBrowser1.Location = New System.Drawing.Point(3, 3)
Me.WebSiteBrowser1.MinimumSize = New System.Drawing.Size(20, 20)
Me.WebSiteBrowser1.Name = "WebSiteBrowser1"
Me.WebSiteBrowser1.Size = New System.Drawing.Size(671, 413)
Me.WebSiteBrowser1.TabIndex = 0
ChangeUserAgent("This is after the url agent gets changed after the first url open")
Me.WebSiteBrowser1.Navigate("http://www.whatsmyuseragent.com/", Nothing, Nothing, "User-Agent: This is the first open url agent")
However whenever I change anything in the visual template it automatically resets back to
Me.WebSiteBrowser1.Dock = System.Windows.Forms.DockStyle.Fill
Me.WebSiteBrowser1.Location = New System.Drawing.Point(3, 3)
Me.WebSiteBrowser1.MinimumSize = New System.Drawing.Size(20, 20)
Me.WebSiteBrowser1.Name = "WebSiteBrowser1"
Me.WebSiteBrowser1.Size = New System.Drawing.Size(671, 413)
Me.WebSiteBrowser1.TabIndex = 0
So my question is where do I write the custom code that would allow me to keep that after the auto-generation from the visual creator?
You should be putting your code in the form1.vb and leave the designer file alone. In the form1.vb[Design] you can change the properties in the properties window. If it is not currently docked to the right side of the VS designer you can show it by going to the Menu -> View -> Properties Window and select it.