Search code examples
c#ms-wordvstooffice-addinsribbonx

How to change getVisible in Ribbon XML if an event happens in thisAddin.cs


So, I've written a Word VSTO Add-in that won't let some users save, print, or change the document based on the document and the user's privileges (I have used DocumentOpen, DocumentBeforePrint, and DocumentBeforeSave Events). The Add-in works fine.

ThisAddIn.cs

public partial class ThisAddIn
    {
        [DllImport("epsede.dll", CharSet = CharSet.Unicode)]
        public static extern int E1(int Event, IntPtr window, [MarshalAs(UnmanagedType.LPWStr)] String FileName);

internal Ribbon1 Ribbon1;
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            Globals.ThisAddIn.Application.DocumentBeforeSave += new Microsoft.Office.Interop.Word.ApplicationEvents4_DocumentBeforeSaveEventHandler(this.Application_DocumentBeforeSave);
        }


protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            Ribbon1 = new Ribbon1();
            return Ribbon1;
        }

        //Before Save
        public void Application_DocumentBeforeSave(Microsoft.Office.Interop.Word.Document Doc, ref bool SaveAsUI, ref bool Cancel)
        {
            IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

            if (E1(2, hWnd, Doc.FullName) == 0)
            {

            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Blocked Saving");
                Cancel = true;
            }
        }

        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
    }
}

Now I want to be able to disable some of the built-in tabs and commands when these events happen in runtime Like if the DocumentBeforeSave event fired and the user is blocked, the SaveAs tab in backstage would become invisible.

I tried to build multiple Ribbon XMLs and use them in my code by switching between them but that only happens once. I have tried to use getVisible and getEnabled but i don't know how to fire them in my events.

This is my Ribbon1.xml: where i have used getvisible for SaveAs Tab.

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">


    <backstage>
        <tab idMso ="TabSave" enabled="false" getVisible="isVisible"/>
    </backstage>


</customUI>

And this is my Ribbon.cs:


namespace WordS
{
    [ComVisible(true)]
    public class Ribbon1 : Office.IRibbonExtensibility
    {
        private Office.IRibbonUI ribbon;

        private bool isVisible;

        public bool IsVisible
        {
            get { return isVisible; }
            set
            {
                isVisible = value;
                ribbon.Invalidate();
            }
        }

        public Ribbon1()
        {
        }

        #region IRibbonExtensibility Members

        public string GetCustomUI(string ribbonID)
        {
            return GetResourceText("WordS.Ribbon1.xml");
        }

        #endregion

        #region Ribbon Callbacks
        //Create callback methods here. For more information about adding callback methods, visit https://go.microsoft.com/fwlink/?LinkID=271226


        public void Ribbon_Load(Office.IRibbonUI ribbonUI)
        {
            this.ribbon = ribbonUI;
        }
        
    }
}

Solution

  • Use the Invalidate/InvalidateControl or InvalidateControlMso methods to get your callback invoked anew.

    For example, if an add-in writer implements the getVisible callback procedure for a button, the function is called once, the state loads, and then if the state needs to be updated, the cached state is used instead of recalling the procedure. This process remains in place until the add-in signals that the cached values are invalid by using the Invalidate method, at which time, the callback procedure is again called and the return response is cached. The add-in can then force an immediate update of the UI by calling the Refresh method.

    An instance of the IRibbonUI interface is passed as a parameter to the onLoad callback.

    In your ribbon XML markup you defined the getVisible callback:

        <backstage>
            <tab idMso ="TabSave" enabled="false" getVisible="isVisible"/>
        </backstage>
    

    But in the code I don't any callback defined for the corresponding attribute specified in the ribbon XML. The callback should have the following signature:

    C#: bool GetVisible(IRibbonControl control)
    
    VBA: Sub GetVisible(control As IRibbonControl, ByRef visible)
    
    C++: HRESULT GetVisible([in] IRibbonControl *pControl, [out, retval] VARIANT_BOOL *pvarfVisible)
    
    Visual Basic: Function GetVisible(control As IRibbonControl) As Boolean
    

    The callback should return the desired value from the callback. So, if you want to hide the controls you need to return false.