Search code examples
c#xmlexcelvstoribbonx

Modifying Office ribbon controls states from elsewhere in the application in VSTO


I have a VSTO Addin ribbon that I made with XML, not the designer. It has a button on it that I only want to have enabled when the user is in a range that the addin controls.

I have the callback setup that works fine when the ribbon is loaded.

Ribbon1.cs code

    public bool refreshButtonState;

    public void Ribbon_Load(Office.IRibbonUI ribbonUI)
    {
        this.ribbon = ribbonUI;
        refreshButtonState = false;
        ribbon.InvalidateControl("btnRefreshQuery");
    }



    public bool refreshEnabled(Office.IRibbonControl control)
    {
        return refreshButtonState;
    }

I captured a worksheet change event to monitor the range the user is in below.

ThisAddIn.cs code

    void ThisWorkbook_SheetChange(object ws, Excel.Range rng)
    {
        //Disable Button state here.

        if (rng.ListObject != null)
        {

            if (rng.ListObject.Name.StartsWith(LO_PREFIX))
            {
                //Enable Button state here.
            }
        }
    }

I can't just pickup the ribbon from Globals.Ribbon.Ribbon1 as it has been suggested many times in the research I've done. Using the XML designer does not grant this functionality.

It seems like a lot of work to move my sheet listening functions over to the ribbon, to me they belong to the addin and not the ribbon anyway as they provide other functionality to the addin. I'm probably missing something obvious, but the documentation that exists is almost non existent. Other than piles of forum questions where the asker or the answerer does not get it.

TLDR: Change a property and invaldate a RibbonX control from code that is not part of the ribbon class.


Solution

  • I figured it out. Or at least a way to get there. I was thinking of parking a listener in the Ribbon class for an event in the Addin class.

    I used this as a reference.

    In my ThisAddin class I created an event.

        public delegate void UpdateRibbon(object source, StateChangeArgs e);
    
        public event UpdateRibbon OnUpdateRibbon;
    

    And the EventArgs class

    public class StateChangeArgs : EventArgs
    {
        private bool EventInfo;
    
        public StateChangeArgs(bool state)
        {
            EventInfo = state;
        }
    
        public bool GetInfo()
        {
            return EventInfo;
        }
    
    }
    

    Added a listener and def to the Ribbon

    Globals.ThisAddIn.OnUpdateRibbon +=new ThisAddIn.UpdateRibbon(ThisAddIn_OnUpdateRibbon);
    

    And

        public void ThisAddIn_OnUpdateRibbon(object a1, StateChangeArgs e)
        {
            refreshButtonState = e.GetInfo();
    
            ribbon.InvalidateControl("btnRefreshQuery");
    
        }
    

    Then raised the event in the Addin

    OnUpdateRibbon(this, new StateChangeArgs(true));
    

    Works great /flex.