I do the tutorial here. Everything works fine with a blank excel page
https://msdn.microsoft.com/en-us/library/bb608590(v=vs.120).aspx
When I load up a excel sheet someone gave me and go to click the toggleButton1 to show the pane I get
{"The taskpane has been deleted or is otherwise no longer valid."}
on the line
private void toggleButton1_Click(object sender, RibbonControlEventArgs e)
{
Globals.ThisAddIn.TaskPane.Visible = ((RibbonToggleButton)sender).Checked;
}
Is a pointer to that task pane somehow going away?
Microsoft.Office.Tools.CustomTaskPane PartPhotoTaskPane;
Globals.ThisAddIn.Application.WindowActivate += Application_WindowActivate;
void Application_WindowActivate(Excel.Workbook Wb, Excel.Window Wn)
{
if (PartPhotoTaskPane != null)
{
PartPhotoTaskPane.Dispose();
InitalizePartPhotoViewerTaskPane(EPPF);
}
else
{
InitalizePartPhotoViewerTaskPane(EPPF);
}
}
/// <summary>
/// Start up the part photo viewer task pane
/// </summary>
private void InitalizePartPhotoViewerTaskPane(ExcelPartPhotoFunctions _EPPF)
{
//intialize the part search
try
{
PartPhotoTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(new PartPhotoSearchPane(_EPPF), "Part Information", Globals.ThisAddIn.Application.ActiveWindow);
PartPhotoTaskPane.Visible = Properties.Settings.Default.InfoPaneOpenStatus;
PartPhotoTaskPane.Width = 260;
}
catch (Exception e)
{
MessageBox.Show("Error starting Part Info Toolbar:" + Environment.NewLine +
e.Message + Environment.NewLine + e.StackTrace, "Error!", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
ATTEMPT 1:
Still error on the click event. I am guessing it is something related to me needing to share that pane between classes?
private void toggleButton1_Click(object sender, RibbonControlEventArgs e)
{
Globals.ThisAddIn.TaskPane.Visible = ((RibbonToggleButton)sender).Checked;
}
private void ExcelEvents_WorkbookActivate(Excel.Workbook wb)
{
var taskPane = (customTaskPanes.Where(kvp => kvp.Key.Target == wb).Select(kvp => kvp.Value).FirstOrDefault());
if (taskPane == null)
{
UcCenlarInvest control = new UcCenlarInvest();
taskPane = this.CustomTaskPanes.Add(control, "My pane for workbook " + wb.Name);
customTaskPanes[new WeakReference(wb)] = taskPane;
}
}
ATTEMPT 2:
So now the Ribbon class can get the TaskPane yet I Still get the same error. Added this :
private CustomTaskPane taskPane;
public CustomTaskPane TaskPane
{
get
{
//return (customTaskPanes.Where(kvp => kvp.Key.Target == wb).Select(kvp => kvp.Value).FirstOrDefault());
return pane;
}
set
{
taskPane = value;
}
}
.....
TaskPane = (customTaskPanes.Where(kvp => kvp.Key.Target == wb).Select(kvp => kvp.Value).FirstOrDefault());
Excel 2016 is a single document interface (SDI), each workbook in a single instance of Excel contains its own ribbon UI. more information
If you open a new workbook, a new ribbon appears, however the pointer to the taskpane is lost. You should implement a WorkbookActivate event and add a new task pane if no task pane already exist for it. You should also keep a static list of pointer to the custom taskpanes.
see this solution : CustomTaskPane in Excel doesn't appear in new Workbooks
You can also work around this by checking the taskpane control's IsDisposed property, for example
if (taskPane.CustomTaskPane?.Control.IsDisposed == false)
{
taskPane.CustomTaskPane.Visible = false;
}
Related
I am currently developping an Excel VSTO addin and here is the code for WorkbookBeforeClose
private void App_WorkbookBeforeClose(Excel.Workbook Wb, ref bool Cancel)
{
bool isEnabled = false;
setRibbonControlState(ref isEnabled);
}
In this code I disable the ribbon if there is no workbook left opened. But if I press the Cancel button from the dialog after I tried to close Excel, the ribbon gets disabled anyway. But as the WorkbookBeforeClose event passed a Cancel parameter, I don't know how to set that parameter when I press the button, how do I check the dialog prompted for the button that has been triggered.
All cases I have seen so far implement a dialog in the body of the WorkbookBeforeClose handler, but I don't want to implement a custom dialog, I would like to use the one provided by default.
Thanks!
As of my VBA experience this
but I don't want to implement a custom
dialog, I would like to use the one provided by default.
is impossible, because that dialog appears just after the before_close event.
The only (known by me) way to manage this stuff - is to create own SaveChanges dialog, which is, by the way, very simple and not every user will notice the difference. And moreover, it will do the same job as default prompt.
The other thing you should pay attention to - is that there may be at least one invisible workbook. Even if you see such screen:
there is a chance that this.Application.Workbooks.Count will show you 1, instead of 0. This is due to the possibility that user has its own Personal.xlsb workbook, which is invisible, but nevertheless, is loaded with an Excel.Application. So in case you want to disable your ribbon properly - you should consider this as well.
Here is my example of this solution:
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookBeforeClose += ApplicationOnWorkbookBeforeClose;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
// Catch the before close event
private void ApplicationOnWorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
{
if (!wb.Saved)
{
switch (MessageBox.Show(text:$"Do you want to save changes you made to '{this.Application.ActiveWorkbook.Name}'?",
caption:"Microsoft Excel",buttons:MessageBoxButtons.YesNoCancel, icon:MessageBoxIcon.Exclamation))
{
case DialogResult.Cancel: // case want to cancel - break the closing event
{
cancel = true;
return;
}
case DialogResult.Yes: // case user want to save wb - save wb
{
wb.Save();
break;
}
case DialogResult.No: // case user don't want to save wb - mark wb as saved to avoid the application messagebox to appear
{
wb.Saved = true;
break;
}
}
}
if (IsAnyWorkbookOpen())
{
// replace this with your code
MessageBox.Show("Some books will still be open, don't turn off the ribbon");
return;
}
// replace this with your code
MessageBox.Show("All books will be closed");
}
private bool IsAnyWorkbookOpen()
{
// check that remaining amount of open workbooks without the one being closed is greater that 2
if (this.Application.Workbooks.Count - 1 > 2)
{
return true;
}
// IF the count of workbooks is 2 one of them maybe a PERSONAL.xlsb
else if (this.Application.Workbooks.Count == 2)
{
foreach (Excel.Workbook wb in this.Application.Workbooks)
{
if (!wb.Name.Equals(this.Application.ActiveWorkbook.Name))
{
// In case when one of two open workbooks is Personal macro book you may assume that
// there will be no open workbooks for user to work directly
if (wb.Name.Equals("Personal.xlsb".ToUpper()))
{
return false;
}
}
}
// In case when NONE of two open workbooks is a Personal macro book
// there will be at least one open workbook for user to work directly
return true;
}
else
{
return true;
}
}
#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
}
And last thing - if you disable your ribbon, but application will still be running - you will have to enable it again on workbook_activate event.
Note I'm just moving from VBA to VSTO - so any comments are highly appreciated.
I have created a PowerPoint VSTO Addin with a custom Task pane - and a ribbon where a toggle button defines the display / hide Status of the custom Task pane. Basis for this was the Microsoft Walkthrough information for custom Task pane and synchronizing the Ribbon with the Task pane.
So fare everything works fine with the first PowerPoint window. I'm able to show the Task pane in the second and third PowerPoint window, but the toggle button on the ribbon only reacts to the last opened / created PowerPoint window and not to the Task pane displayed / hidded in the active PowerPoint window.
I've found another thread which explains exactly the same Problem here:
C# VSTO-Powerpoint-TaskPanes in separate windows.
But I don't understand the answer neither I don't know how to implement a PowerPoint Inspector Wrapper.
I'm new in C# and just getting a keyword like "Inspector Wrapper" is to less for me. I already spend hours in searching the net but wasn't successfull till now.
Is there a chance to get a COMPLETE code example for PowerPoint how this works, what has to be done?
Code added:
I took the code from the General walkthrough: https://msdn.microsoft.com/en-us/library/bb608590.aspx and changed it with an Event for new presentations:
The code for the ThisAddIn.cs is as follow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using Office = Microsoft.Office.Core;
namespace PowerPointAddIn1
{
public partial class ThisAddIn
{
private TaskPaneControl taskPaneControl1;
private Microsoft.Office.Tools.CustomTaskPane taskPaneValue;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.AfterNewPresentation += new Microsoft.Office.Interop.PowerPoint.EApplication_AfterNewPresentationEventHandler(NewPresentation);
//taskPaneControl1 = new TaskPaneControl();
//taskPaneValue = this.CustomTaskPanes.Add( taskPaneControl1, "MyCustomTaskPane");
//taskPaneValue.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
}
void NewPresentation(Microsoft.Office.Interop.PowerPoint.Presentation oPres)
{
PowerPoint.Application app = this.Application;
PowerPoint.DocumentWindow docWin = null;
foreach (PowerPoint.DocumentWindow win in Globals.ThisAddIn.Application.Windows)
{
if (win.Presentation.Name == app.ActivePresentation.Name)
{
docWin = win;
}
}
this.taskPaneControl1 = new TaskPaneControl();
this.taskPaneValue = this.CustomTaskPanes.Add(taskPaneControl1, "MyCustomTaskPane", docWin);
this.taskPaneValue.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
}
private void taskPaneValue_VisibleChanged(object sender, System.EventArgs e)
{
Globals.Ribbons.ManageTaskPaneRibbon.toggleButton1.Checked =
taskPaneValue.Visible;
}
public Microsoft.Office.Tools.CustomTaskPane TaskPane
{
get
{
return taskPaneValue;
}
}
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
}
}
I remember the learning curve oh so well. Here's a sample that I believe addresses your issue. You need to link the task pane to the document. I relied on the naming scheme for new documents here, but a DocumentVariable would be a much better choice (they are discarded at the end of the current session). Add a variable to the presentation, store the task pane id in the control, and compare them to get the right task pane.
You need an XML ribbon (could probably use a Ribbon Designer but those are not as good). I removed some of the boilerplate and irrelevant code from this.
ThisAddIn.cs:
namespace PowerPointAddIn1
{
public partial class ThisAddIn
{
public static int counter = 0;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.AfterNewPresentation += Application_AfterNewPresentation;
}
private void Application_AfterNewPresentation(PowerPoint.Presentation Pres)
{
int count = ++counter;
UserControl1 uc = new UserControl1("task pane " + count);
CustomTaskPane ctp = CustomTaskPanes.Add(uc, "custom task pane " + count);
ctp.Visible = true;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new Ribbon1();
}
}
}
Ribbon1.cs:
namespace PowerPointAddIn1
{
[ComVisible(true)]
public class Ribbon1 : Office.IRibbonExtensibility
{
private Office.IRibbonUI ribbon;
public Ribbon1()
{
}
public void toggleTaskPane(Office.IRibbonControl control, bool enabled)
{
var CTPs = Globals.ThisAddIn.CustomTaskPanes;
var pres = Globals.ThisAddIn.Application.ActivePresentation;
foreach (var x in CTPs)
{
if (pres.Name.EndsWith(x.Title.Replace("custom task pane ", "")))
{
x.Visible = enabled;
}
}
}
public bool isPressed(Office.IRibbonControl control)
{
var CTPs = Globals.ThisAddIn.CustomTaskPanes;
var pres = Globals.ThisAddIn.Application.ActivePresentation;
foreach (var x in CTPs)
{
if (pres.Name.EndsWith(x.Title.Replace("custom task pane ", "")))
{
return x.Visible;
}
}
return false;
}
}
}
Ribbon1.xml:
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<ribbon>
<tabs>
<tab idMso="TabAddIns">
<group id="MyGroup"
label="My Group">
<checkBox id="mycheckbox" label="show task pane" onAction="toggleTaskPane" getPressed="isPressed" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
UsreControl1.cs (just has a label on it):
namespace PowerPointAddIn1
{
public partial class UserControl1 : UserControl
{
public UserControl1(string labelValue)
{
InitializeComponent();
label1.Text = labelValue;
}
}
}
I just want to share my results which works now for me (Thanks to Chris who gave me some valuable inputs). I do have a customtaskpane management which works for each presentation. The only Thing which is not yet implemented is if a user opens the document in a separate window (View / New Window). This one I don't know how to manage.
As fare as I can test it this works now.
here is the link to the whole solution:
https://happypc-my.sharepoint.com/personal/roger_heckly_happy-pc_ch/_layouts/15/guestaccess.aspx?docid=0426d40dc5df74d66ba42a3b928111ce8&authkey=Aa6yX6QWUnqXp1jcUfGveL8
Please be Aware - I'm beginner - so if you have feedback / inputs please let me know. For sure, some code could be written easier etc.
I'm working on a feature which is to create a backup when a open word saved each times.
I'm using the blow code to hooking into word process and bind events to it, the word is opened by process.
officeApplication = (Application)Marshal.GetActiveObject("Word.Application").
officeApplication.DocumentBeforeSave += new ApplicationEvents4_DocumentBeforeSaveEventHandler(App_BeforeSaveDocument);
And in App_BeforeSaveDocument I did my work.
I get officeApplication right, and bind events were fine, when I click save in word, the events triggered perfectly.
The problem is, a few seconds(may be 30s) after, the events will not fire anymore, no matter click save or save us or close document.
Is there any suggestions?
After a lot of researching, I still can't find the reason. And I decide to use a trick to approach it.
First, open a thread in the binding event:
static void App_BeforeSaveDocument(Microsoft.Office.Interop.Word.Document document, ref bool saveAsUI, ref bool cancel)
{
if (th != null)
th.Abort();
th = new Thread(backupOnSave);
th.IsBackground = true;
th.Start(document);
}
Then do an infinity loop in the thread:
internal static void backupOnSave(object obj)
{
try
{
Application app = obj as Application;
if (app == null || app.ActiveDocument == null)
{
return;
}
Microsoft.Office.Interop.Word.Document document = app.ActiveDocument;
if (!tempData.ContainsKey(document.FullName))
return;
var loopTicks = 2000;
while (true)
{
Thread.Sleep(loopTicks);
if (document.Saved)
{
if (!tempData.ContainsKey(document.FullName))
break;
var p = tempData[document.FullName];
var f = new FileInfo(p.FileFullName);
if (f.LastWriteTime != p.LastWriteTime)//changed, should create new backup
{
BackupFile(p, f);
p.LastWriteTime = f.LastWriteTime;
}
}
}
}
catch (Exception ex)
{
log.write(ex);
}
}
And it works fine. Don't remember to abort the thread when the document closed or exception happen.
Background
I'm currently working on an application in VSTO2015 and Excel 2016. The application manages a number of CustomTaskPanes in different windows. I am trying to get // some code to fire when the task pane is opened or closed. In order to handle the various windows, I've implemented a structure very similar to this;
CustomTaskPane in Excel doesn't appear in new Workbooks
ThisAddIn.cs contains the following class;
public class TaskPaneManager
{
static Dictionary<string, Microsoft.Office.Tools.CustomTaskPane> _createdPanes = new Dictionary<string, Microsoft.Office.Tools.CustomTaskPane>();
/// <summary>
/// Gets the taskpane by name (if exists for current excel window then returns existing instance, otherwise uses taskPaneCreatorFunc to create one).
/// </summary>
/// <param name="taskPaneId">Some string to identify the taskpane</param>
/// <param name="taskPaneTitle">Display title of the taskpane</param>
/// <param name="taskPaneCreatorFunc">The function that will construct the taskpane if one does not already exist in the current Excel window.</param>
public static Microsoft.Office.Tools.CustomTaskPane GetTaskPane(string taskPaneId, string taskPaneTitle, Func<UserControl> taskPaneCreatorFunc)
{
string key = string.Format("{0}({1})", taskPaneId, Globals.ThisAddIn.Application.Hwnd);
string title = taskPaneId;
string windowId = Globals.ThisAddIn.Application.Hwnd.ToString();
if (!_createdPanes.ContainsKey(key))
{
var customTaskPane = taskPaneCreatorFunc();
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
_createdPanes[key] = pane;
//
// Set the task pane width as set in the App.Config
//
pane.Width = Convert.ToInt32(ConfigurationManager.AppSettings["TaskPaneWidth"]);
}
return _createdPanes[key];
}
....
My calls from Ribbon.cs;
private void btnUploadWizard_Click(object sender, RibbonControlEventArgs e)
{
// Check for configuration sheet first.
string title = "Upload Wizard";
TaskPaneManager.isConfigurationCreated();
var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());
UploadWizardTaskpane.Visible = !UploadWizardTaskpane.Visible;
}
The Problem: Event Handlers
I'm having difficulty getting an event handler to fire. I can't tell what I'm doing wrong. In the TaskPaneDesigner I am attaching the event using this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged);, and then defining it in my TaskPaneUploadWizard class as follows;
public partial class TaskPaneUploadWizard : UserControl
{
...
public TaskPaneUploadWizard()
{
InitializeComponent();
}
private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
{
// Some code
}
My thoughts
It seems to me as though I am either attaching the eventHandler to something other than the CustomTaskPane object, or I am attaching it before the CustomTaskPane is created.
Help!
To detect if the task pane was opened or closed, you have to attach to the VisibleChanged event of pane.
So the simplest solution would be to add just one line of code to the GetTaskPane method:
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
// This is the new line to be added.
pane.VisibleChanged += (s, e) => customTaskPane.Visible = pane.Visible;
_createdPanes[key] = pane;
Now the visibility of the whole task pane will be passed on to its content and // some code should be executed.
Alternatively, if you don't want to manually set customTaskPane.Visible for whatever reason, you could as well execute your code directly in this new event handler:
pane.VisibleChanged += (s, e) => { /* some code */ };
But personally I would rather recommend the first approach because it seems to fit a bit better into your existing code.
var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());
This is creating a CustomTaskPane not a TaskPaneUploadWizard object
It seems like this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged); is acting on TaskPaneUploadWizard which is only the guest of CustomTaskPane
Note that The visibility of CustomTaskPane doesn't affect TaskPaneUploadWizard
My suggestion
You remove VisibleChanged from the designer, you will add it manually in your TaskPaneUploadWizard
public partial class TaskPaneUploadWizard : UserControl
{
//{---}
CustomTaskPane _HostPane;
public CustomTaskPane HostPane
{
get => _HostPane;
set
{
if(_HostPane == value)
return;
_HostPane?.VisibleChanged -= TaskPaneUploadWizard_VisibleChanged;
_HostPane = value;
_HostPane?.VisibleChanged += TaskPaneUploadWizard_VisibleChanged;
}
}
//{---}
private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
{
// Some code
}
//{---}
Then in GetTaskPane you say
//{---}
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
(customTaskPane as TaskPaneUploadWizard).HostPane = pane;
//{---}
I have a C# VSTO application with .NET 4.0 that uses two custom CommandBarButtons in the Globals.ThisAddIn.Application.CommandBars["Cell"] context menu. I create the buttons once on the ThisAddIn_Startup event and they work wonderfully for all workbooks. If my add in is shut down at some point without shutting down Excel then an issue occurs if there's more than 1 open workbook. Only the active workbook's right click context menu has the buttons deleted.
The code:
public partial class ThisAddIn
{
private const string TAG_PASTE_EVENT = "PASTE EVENT";
private const string TAG_COPY_EVENT = "COPY EVENT";
private Office.CommandBarButton copy_event, paste_event;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
try
{
DefineShortcutMenu();
/*
* Other start up stuff that works fine
*/
}
catch (Exception ex)
{
ExceptionHandler.HandleException(ex);
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
/*
* Other shut down stuff that works fine
*/
// Remove the right click menu buttons
foreach (Office.CommandBarControl control in Application.CommandBars["Cell"].Controls)
{
if (control.Tag.Equals(TAG_PASTE_EVENT))
{
control.Delete(true);
}
else if (control.Tag.Equals(TAG_COPY_EVENT))
{
control.Delete(true);
}
}
}
private void copy_event_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
// Copy code
}
private void paste_event_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
// Paste code
}
private void DefineShortcutMenu()
{
// Create and add the paste button
paste_event = (Office.CommandBarButton)Application.CommandBars["Cell"].Controls.Add(Office.MsoControlType.msoControlButton, missing, missing, 1, true);
paste_event.Style = Office.MsoButtonStyle.msoButtonCaption;
paste_event.Caption = "Paste Event";
paste_event.Tag = TAG_PASTE_EVENT;
paste_event.DescriptionText = "Stuff happens";
paste_event.Enabled = false;
paste_event.Click += paste_event_Click;
// Create and add the copy button
copy_event = (Office.CommandBarButton)Application.CommandBars["Cell"].Controls.Add(Office.MsoControlType.msoControlButton, missing, missing, 1, true);
copy_event.Style = Office.MsoButtonStyle.msoButtonCaption;
copy_event.Caption = "Copy Event";
copy_event.Tag = TAG_COPY_EVENT;
copy_event.DescriptionText = "Stuff happens";
copy_event.Enabled = false;
copy_event.Click += copy_event_Click;
}
#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
}
I'm using Excel 2013 which I know has one bug related to the right click context menu (which is why I'm using a foreach for the CommandBarControls instead of using the global variables). Any workflows that you've found that work would be much appreciated!
TO CLARIFY: Everything works fine, the only issue is CommandBarButtons not deleting from the right click context menus of the non-active workbooks if the Add In is shut down. If the Add In is turned back on during the same session all the workbooks are given the Copy and Paste buttons again meaning the workbooks whose context menus didn't update properly now have 2 Copy buttons and 2 Paste buttons.
Command Bars were deprecated with Office 2010. You need to use the Fluent UI controls instead.
See Customizing Context Menus in Office 2010 for more information.