Excel Custom Task Pane not showing - excel

I'm showing a custom task pane in an excel VSTO add-in, I'm building it and showing it as thus:
var ctrl = new CellTaskPane();
var pane = CustomTaskPanes.Add(ctrl, "Custom Sheet");
pane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight;
pane.DockPositionRestrict = Office.MsoCTPDockPositionRestrict.msoCTPDockPositionRestrictNoChange;
pane.Visible = true;
This is being done in the ThisAddin.cs file and it working just fine on my machine, both under a Debug session and with the add-in installed via the click-once installer.
However, installing the add-in on a colleague's machine is proving troublesome.
The add-in is functioning and the context menu / ribbon is working perfectly, but the pane just refuses to show.
I have a toggle button on the ribbon which toggles the Visible property on the pane and even clicking that isn't forcing the pane to show.
Any help on this would be greatly appreciated, Google is proving useless for this.
Thanks.
I should mention that CellTaskPane is just a UserControl as per the docs on MSDN: http://msdn.microsoft.com/en-us/library/aa942846.aspx

Turns out it wasn't anything we were doing directly!
There was another add-in installed (third party) which for some bizarre reason was interfering with the pane being shown (no idea why or how).
Shame that Excel doesn't show any sort of error or at least throw an exception.
Ah well.

I suggest you try a very, very simple custom task pane first to see if this works. I put together the most simple example I could think of, basically a single text box that gets a value pushed into it and this is returned to the ribbon when a button is pushed.
If you were to try this then I would do it as a new solution. Create a new VSTO project with a "Designer mode" ribbon. Add a toggle button and a normal button below it. Then copy in this code:
ThisAddIn.cs
using System;
using Office = Microsoft.Office.Core;
namespace ExcelAddIn1
{
public partial class ThisAddIn
{
private Microsoft.Office.Tools.CustomTaskPane pane;
private CellTaskPane ctrl = new CellTaskPane();
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
pane = CustomTaskPanes.Add(ctrl, "Custom Sheet");
pane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight;
pane.DockPositionRestrict = Office.MsoCTPDockPositionRestrict.msoCTPDockPositionRestrictNoChange;
pane.Visible = true;
pane.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
ctrl.SetName("test");
}
private void taskPaneValue_VisibleChanged(object sender, System.EventArgs e)
{
Globals.Ribbons.Ribbon1.toggleButton1.Checked = pane.Visible;
}
public Microsoft.Office.Tools.CustomTaskPane TaskPane
{
get
{
return pane;
}
}
public CellTaskPane MyContainer
{
get
{
return ctrl;
}
}
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
}
}
Add a new class called CellTaskPane.cs:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
namespace ExcelAddIn1
{
public class CellTaskPane : System.Windows.Forms.UserControl
{
public System.Windows.Forms.TextBox test;
public CellTaskPane()
{
InitializeComponent();
}
public void InitializeComponent()
{
test = new System.Windows.Forms.TextBox();
test.Location = new System.Drawing.Point(120, 8);
test.Size = new System.Drawing.Size(232, 20);
test.TabIndex = 0;
Controls.AddRange(new System.Windows.Forms.Control[] { test });
Size = new System.Drawing.Size(375, 150);
}
public void SetName(string text)
{
test.Text = text;
}
public string GetName()
{
return test.Text;
}
}
}
Add the following code to Ribbon1.cs:
using System;
using Microsoft.Office.Tools.Ribbon;
namespace ExcelAddIn1
{
public partial class Ribbon1
{
private void toggleButton1_Click(object sender, RibbonControlEventArgs e)
{
Globals.ThisAddIn.TaskPane.Visible = ((RibbonToggleButton)sender).Checked;
}
private void button1_Click(object sender, RibbonControlEventArgs e)
{
button1.Label = Globals.ThisAddIn.MyContainer.GetName();
}
}
}
Obviously you will need to do a little tweaking to get this to work, I tried to take the default names for a new project and the buttons.
When you run this you should get a custom task pane. When you switch to the "TabAddIn" and click the toggle button it should show/ hide the task pane. When you click the normal button the contents of the only field in the task pane should be copied as the button name. I defaulted this to "test", so even if the task pane isn't visible you can see if it is in memory or not?
I tested this and it appears to work fine. Basically this is just a hacked up version of the examples on MSDN. If you wanted you could probably do this yourself anyway? If nothing else this will enable you to see if there is anything in the more complex ribbon you are working on that causes issues... or if this is a fundamental problem with your colleague's machine.

I had the same problem, but it was not any addin I could disable (COM+ or Excel).
I had my excel configured to open files at startup
(Excel Options -> Advanced -> General)
There, I had an .XLAM that customized the ribbon.
When I cleared this configuration, my addin started working.

I ran into exactly this problem while trying to get the Microsoft sample code for "Walkthrough: Synchronizing a Custom Task Pane with a Ribbon Button" working. Here's a link to the page:
http://msdn.microsoft.com/en-us/library/bb608590.aspx
After starting from scratch about three times and scouring the Internet for a clue as to what I might have been doing wrong, I came across this question and Clint's answer that an add-in was causing his problem. I had a few add-ins enabled, but with some trial and error I found the culprit: Microsoft's own "Analysis Toolpack"!
Once I disabled Analysis Toolpack, the custom pane started appearing and disappearing as expected.
So, as Clint discovered, the first thing you should probably try if you run into this issue is to disable all add-ins and see if that does the trick. If so, then you can go back and begin turning them on until you find the one that is interfering with your custom pane visibility.

Well, after following #GaryP's advice, disabling my other add-ins, and thinking that I'd solved the problem (albeit without access to my other add-ins), I discovered that the add-in would disappear whenever I opened more than one workbook.
But at that point, I didn't just get a missing taskpane or a silent fail, I actually got an error:
The taskpane has been deleted or is otherwise no longer valid
So it seems that disabling add-ins isn't solving the problem in itself, but rather disabling add-ins is reducing the number of open workbooks (even if add-ins aren't visible, they can still have a Ribbon handle)...
The underlying cause is the use of SDI in 2013 and later.
So, now I can have all of my add-ins loaded.

Create a new instance of the task pane for each workbook. Make the following changes to your code and the task pane works even with addins enabled.
private void Application_WorkbookActivate(Microsoft.Office.Interop.Excel.Workbook wb)
{
pane = CustomTaskPanes.Add(ctrl, "Custom Sheet");
pane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight;
pane.DockPositionRestrict = Office.MsoCTPDockPositionRestrict.msoCTPDockPositionRestrictNoChange;
pane.Visible = true;
pane.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
ctrl.SetName("test");
}
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookActivate += new Excel.AppEvents_WorkbookActivateEventHandler(
Application_WorkbookActivate);
}

If you find that even after closing all other add-ins, the TaskPane still doesn't show, it may be because it is loaded in your "Personal.xlsb" workbook. After closing it, I tried making the pane visible again and I received an error that it had been closed.

I had the same problem and didn't fix it by disableing the analysis toolpack, but, rather i had to move the XLAM out of its installed folder (Break the reference to it, since you couldn't remove it through Excel) and it started working.
I've sense added the files back and it continues to work. Activating the addin does cause my custom taskbar to break. Not sure what this long term fix is here.

I know this is very old, but it can be useful for anyone who may look up for an answer, but here we go:
if you are adding the new taskpane under ThisAddIn_Startup, it will only add it once at the start of the excel, and it will not be present for any other Excel session, so based on the following link that shows how to handle multiple sessions:
https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/bb264456(v=office.12)?redirectedfrom=MSDN#Anchor_2
I came up with the conclusion that I should create a new taskpane under the other events that can fire when I needed the taskpane, then validate if the current window has the taskpane or not, and create a new one if not and show it. the event can be any trigger like ribbon button, open document, etc.
Dim CurrentTaskPane As Microsoft.Office.Tools.CustomTaskPane = Nothing
Globals.ThisAddIn.RemoveOrphanedTaskPanes() 'to remove any unused taskpane
For Each ctp As Microsoft.Office.Tools.CustomTaskPane In Globals.ThisAddIn.CustomTaskPanes
If ctp.Window.Hwnd = Excel.Application.ActiveWindow.Hwnd Then
CurrentTaskPane = ctp
Exit For
End If
Next
If CurrentTaskPane Is Nothing Then
CurrentTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(New ControlName, "My TaskPane", Excel.Application.ActiveWindow)
End If
CurrentTaskPane.Visible = True
You can find the 'RemoveOrphanedTaskPanes' code from the link provided.

Summarizing other answers: It appears that this is due to loading other Add-ins, .XLAM files, etc. These can be loaded from many different places, and you need to check them all, and remove them. You may be able to reenable them later, so back everything up. Here is a checklist:
File -> Options -> Advanced -> General -> "At startup, open all files in..." Remove those files and disable the option.
File -> Options -> Add-ins -> Check all the Active application add-ins. Near the bottom of the dialog, use the Mange: Excel/Com Add-ins to view the Add-ins and disable or remove them. Even the Microsoft included ones could be causing it, so disable them as well. They do not allow you to remove them, which is fine, as long as they are disabled.
C:\Users\$USERNAME\AppData\Roaming\Microsoft\Excel\XLSTART Remove all files from this directory.
C:\Users\$USERNAME\AppData\Roaming\Microsoft\AddIns Remove all files from this directory.
After this, try again to load the Add-In. Then, add back the things you need one by one and keep testing. They may break it again, or not, there does not appear to be consensus on what kind of Add-In does and does not break the task pane.
If anyone discovers more places to look for Add-Ins, I will add them to the list.

Related

Excel AddIn Issue on Workbooks.Open() being stuck on Excel Home Screen

Previously this code for opening and activating(bringing workbook to front) has worked for other builds of Microsoft office, however Microsoft's latest build(16.0.11126.20234) seemed to create a problem with opening an existing excel workbook.
testWorkbook = this.Application.Workbooks.Open(path);
testWorkbook.Activate();
The issue is when launching excel and using an 'Open' method on a workbook:
Excel application launches
The title of on the top of the window changes to the workbook name you are trying to open, however the main body of the window is still the default Excel Home screen( home, options, recent workbooks, templates)
Selecting options or any other item on the splash screen results in the splash screen closing and displays the workbook that was intended to be open
I've tried removing any existing code, and created a new AddIn solution with just the code Microsoft documentation provides for loading and displaying a workbook.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Workbook testWorkbook;
string path = "C:\\Path\\To\\Workbook\\TestingBookOpen.xlsx";
try
{
testWorkbook = this.Application.Workbooks.Open(path);
testWorkbook.Activate();
}
catch (Exception ex){
string message = ex.Message;
}
}
I know there are workarounds including:
You can disable the start screen from excel directly.
You can click into options and then exit it which will bring the sheet up
You can have excel already open and it'll work fine
But I'm looking for a programmatic solution/workaround for when my AddIn launches, and to see whether other's have come across the same problem in Microsoft Office's latest build.

Save button is disabled on edit form, cannot click it via UIAutomation

This issue is in the same application as my last question.
I'm experiencing some "odd" behavior, and I don't know if it's functioning as designed or if there's something wrong. In the 3rd party application that I'm "injecting" data into I experience different behavior when I manually edit a record vs. when my automated application edits a record.
If I run the 3rd party application and manually edit a record, the 'Save' and 'Undo' buttons in the toolbar become enabled once I begin typing in a field.
If I run the 3rd party application, then run my automated application to edit a record, my application sets focus to the first field on the form, then "injects" the data into the fields (it actually looks like someone is typing it in very fast) but the 'Save' and 'Undo' buttons stay disabled the whole time. I try to invoke the 'Save' button when I reach the bottom of the form, but I receive an error:
"An unhandled exception of type 'System.Windows.Automation.ElementNotEnabledException' occurred in UIAutomationClient.dll
Additional information: The operation is not allowed on a nonenabled element."
I used this code example on MSDN to insert text in the textboxes of the 3rd party application.
if (!element.TryGetCurrentPattern(
ValuePattern.Pattern, out valuePattern))
{
// Set focus for input functionality and begin.
element.SetFocus();
// Pause before sending keyboard input.
Thread.Sleep(100);
// Delete existing content in the control and insert new content.
SendKeys.SendWait("^{HOME}"); // Move to start of control
SendKeys.SendWait("^+{END}"); // Select everything
SendKeys.SendWait("{DEL}"); // Delete selection
SendKeys.SendWait(value);
}
// Control supports the ValuePattern pattern so we can
// use the SetValue method to insert content.
else
{
// Set focus for input functionality and begin.
element.SetFocus();
((ValuePattern)valuePattern).SetValue(value);
}
Perhaps I'm not searching the correct keywords, but Google has not been much help to me, and I only found one SO post that seemed related. If anyone can shed some light on this I'd really appreciate it.
TIA
UPDATES:
Re: why don't I check if button is enabled...
I don't know how to accomplish that, I was trying to do that earlier today. I have an AutomationElement that references the 'Save' button, but the AutomationElement doesn't have an Enabled property.
AutomationElement toolbar = _mainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "59392"));
AutomationElement saveButton = toolbar.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Commit Changes (CTRL S)"));
I tried to cast the AutomationElement as Button to check the Enabled property but I receive a build error "Cannot convert type 'System.Windows.Automation.AutomationElement' to 'System.Windows.Forms.Button'"
if (!((Button)saveButton).Enabled)
((Button)saveButton).Enabled = true;
...ok, more searching lead to this: UIAutomation Button Style Enabled
so now I can check if it is enabled, but I haven't figured out how to enable it yet.
2014.10.02 - I think it's not possible to enable a disabled button via UIAutomation. So I will modify my question a bit. If I manually click on an input field and begin typing the 'Save' button becomes enabled. If I use UIAutomation to modify the record the 'Save' button does not become enabled. So, how can I use UIAutomation to get the window into the same state that it's in when I manually edit the record?
You could try using the win32 api to send messages to the button directly. You can get all the handles you need from uiautomation just use the post message function like this.
public TestSetup AutomationLibrary;
[DllImport("User32.dll")] //http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx
public static extern int PostMessage(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
public int WM_ENABLE = 0x0A;
public int WM_COMMAND = 0x111;
public MainWindow()
{
InitializeComponent();
AutomationLibrary = new TestSetup();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AutomationElement aeDesktop = AutomationLibrary.GetDesktop();
AutomationElement aeForm1 = AutomationLibrary.GetAutomationElement("Form1", aeDesktop);
AutomationElement aeDisabledButton = AutomationLibrary.GetAutomationElement("DISABLED", aeForm1);
IntPtr windowIntPtr = new IntPtr(aeForm1.Current.NativeWindowHandle);
IntPtr controlIntPtr = new IntPtr(aeDisabledButton.Current.NativeWindowHandle);
PostMessage(windowIntPtr, WM_COMMAND, (IntPtr)WM_ENABLE, controlIntPtr);
}
The TestSetup is my own home brewed automationlibrary but basically I am just getting automation elements then using the PostMessage function from User32.dll to enable the button. although it doesnt seem to actually enable the button, it does have the unintend consequence of clicking the button.
I used this article as reference for the postmessage function http://www.codeproject.com/Articles/487938/Re-Active-Disabled-Controls
Hope this helps

Determining which Visual Studio context menu was selected?

I'm writing a VS2012 add-in, adding a command to Build Explorer context menu (see related question). The command gets added to 2 different context menus:
Build Explorer
Team Explorer, Builds page, My Builds section
When my one callback is called, how do I know which of these it is?
I tried get the focused control (using P/Invoke as this question suggests). However, it gets me a Tabs container for (1), and null for (2). I could try to cast the control to the tabbed container, but that sounds pretty bad...
Any better alternative?
My new/other idea - it is similar to yours:
You should try to monitor which window was activated lastly.
If you create an eventhandler for your command, then you may be able to check which window is active when your command fired. A simple evenent handler for a command:
void cmdEvents_BeforeExecute( string guid, int ID, object customIn, object customOut, ref bool cancelDefault )
{
Window2 teamExplorer = _applicationObject.Windows.Item("Team Explorer") as Window2;
if (_applicationObject.ActiveWindow.Caption == teamExplorer.Caption)
{
//You are called from Team Explorer
}
else
{
//Somewhere else
}
}
And the way you can subscribe:
static _dispCommandEvents_BeforeExecuteEventHandler _myHandler;
static CommandEvents _cmdEvents;
public void OnConnection(...)
{
Command command = ...; // Init your command
int ID = command.ID;
string GUID = command.Guid;
CommandEvents _cmdEvents = _applicationObject.Events.get_CommandEvents(GUID, ID);
_myHandler = new _dispCommandEvents_BeforeExecuteEventHandler(cmdEvents_BeforeExecute);
_cmdEvents.BeforeExecute += _myHandler;
}
You may find a better way to identify the window(s) by GUID. You should keep at least _cmdEvents as static because when it will be desroyed, your event handler could vanish (least for internal commands).
In OnDisconnection you should unsubscribe.
Reworked by the comment, and founded links:
As the menu item is shown every place it seems there is no way to distinct between them from an Add-In, you should add two command and distinct them by their context.
The way instead of converting the Add-In to a VS-Package MZ-Tools HOWTO: Controlling the state of command in a Visual Studio add-in, try MZ-Tools HOWTO: Use the IVsMonitorSelection ... you can also get it from an Add-In.
But:
Neither the AddNamedCommand nor the QueryStatus methods honor the
invisible state: the button that must be invisible ...
remains disabled rather than invisible.
I think this makes it impossible to do it from an Add-In on a suitable way, but maybe you can check the contexts.
Other way you could get further, if you try to migrate your command/menu into a VSPackage and create a custom UIContext for the menu items or find a suitable predefined one. I have no access to a Studio enhanced with Build Explorer so I can't try it.
The following discussion is about custom contexts for vs-packages:
http://davedewinter.com/2008/04/05/dynamic-menu-commands-in-visual-studio-packages-part-3/
Sadly the links are broken from the post, and I can't reach Part 1. and Part 2. which is about the discussion of the problem from the beginning.
But there is no guarantee you can create a context which suits you.
Only context ID I found for Team Explorer is the guidTeamProjectCmdUIContext.
It is placed at vsshilds.h in Visual Studio 2010 SDK, vsshell*.h are also contain several others.
MSDN: Vsct files to define command, menus, ect. from packages.
Condition attribute for items:
http://msdn.microsoft.com/en-us/library/bb491718.aspx
http://msdn.microsoft.com/en-us/library/bb166515.aspx
MSDN: VisibilityItem element for commands and toolbars.
VisibilityItem element determines the static visibility of commands and toolbars.
... After the VSPackage is loaded, Visual Studio expects command visibility to be determined by the VSPackage rather than the VisibilityItem.
And finally about predefined Context Guids:
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.uicontextguids80.aspx
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.uicontextguids.aspx

Routed Event Args with ButtonBase

I am currently working on a program with a team of members and they don't know how to solve this either. I created a set of specialty user controls "ImageButton". I am using that for the menu section of the program. The menu itself is another user control.
They are currently using ButtonBase.Click to access the buttons, but it doesn't work with my new ImageButtons.
The ButtonBase.Click leads to an event called "MainMenu_Click".
private void MainMenu_Click(object sender, RoutedEventArgs e)
{
Button _menuButtonClicked = (Button)e.OriginalSource;
this.ShowMainPage((MainMenuCommands)_menuButtonClicked.Tag);
}
I have already tried parsing them as ImageButtons but it has not worked. So I need a solution that is similar to ButtonBase.Click but for my ImageButtons.
Edit:
There are 3 files I am working with:
MainWindow.xaml (and its cs)
MainMenu.xaml (and its cs)
ImageButton.xaml (and its cs)
MainMenu has several ImageButtons on it. MainMenu is used on MainWindow. One of the properties to set is ButtonBase.Click. But I need that to be setup for ImageButtons not System.Windows.Controls.Button.
You would need to make your ImageButton derive from ButtonBase (or Button) -- then you'll get all of ButtonBase such as its events like Click. It's actually quite simple to do this. I have a RibbonButton that's like an image button which derives from Button as a custom control. You write the template in Generic.xaml to set the look, then add any other properties and code.

Excel VSTO - Disable RibbonButton during Cell Edit

I am coding an Excel add in and would like my ribbon buttons to be disabled when the user is clicked into (editing) a cell. I see this happen with other ribbon buttons in Excel and in the TFS Excel add in so I'm hoping there's some way that other developers can accomplish it. Is there some way to determine/handle when an edit begins/ends (i.e. via double clicking/hitting Enter/hitting Escape)?
I was thinking I could manually enable/disable the buttons during these events. I was wondering if the technique described here http://www.codeproject.com/KB/office/Excel_Edit_Mode.aspx is the best way to detect if a cell is currently being edited?
Any guidance would be greatly appreciated.
Thanks
Sorry for the late answer. I just came across your question.
I wanted the same behavior in my project and I succesfuly integrated this solution:
http://www.codeproject.com/KB/office/ExcelInEditMode.aspx
It was painless and worked very well. I just added the class in my project, created an instance of it and attached events to the EditModeOff and EditModeOn events.
Hope that helps (you or someone else)
Here is what I have done to accomplish the same goal as you.
I created a Timer set for 500ms
In the event handler for the Timer, I perform the following check
if (Globals.ThisAddIn.Application.Ready)
{
SetRibbonState()
}
Where SetRibbonState is something like this:
private void SetRibbonState()
{
if (IsEditing())
{
buttonRefresh.Enabled = false;
}
else
{
buttonRefresh.Enabled = true;
}
}
public static bool IsEditing()
{
return !Globals.ThisAddIn.Application.CommandBars.GetEnabledMso(FileNewDefaultIdMso);
}

Resources