Search code examples
visual-studio-extensionsvs-extensibilityvspackage

how to update Visual Studio UI when using DynamicItemStart inside a vsix package


I'm implementing a DynamicItemStart button inside a Menu Controller. I'm loading the dynamic items for this button when Visual Studio starts. Everything is loaded correctly so the initialize method is called an I see all the new items in this Dynamic button. After the package is completely loaded I want to add more items to this Dynamic button, but since the package is already loaded the initialize method is not called again and I cannot see the new items in this Dynamic button. I only see the ones that were loaded when VS started.

Is there any way that I can force the update of this Dynamic button so it shows the new items?. I want to be able to update the VS UI after I added more items but outside the Initialize method. The implementation I did is very similar to the one showed on this msdn example:

http://msdn.microsoft.com/en-us/library/bb166492.aspx

Does anyone know if an Update of the UI can be done by demand?

Any hints are greatly appreciated.


Solution

  • I finally got this working. The main thing is the implementation of a derived class of OleMenuCommand that implements a new constructor with a Predicate. This predicate is used to check if a new command is a match within the DynamicItemStart button.

    public class DynamicItemMenuCommand : OleMenuCommand
    {
    private Predicate<int> matches;
    public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
      : base(invokeHandler, null, beforeQueryStatusHandler, rootId)
    {
      if (matches == null)
      {
        throw new ArgumentNullException("Matches predicate cannot be null.");
      }
    
      this.matches = matches;
    }
    
    
    public override bool DynamicItemMatch(int cmdId)
    {
      if (this.matches(cmdId))
      {
        this.MatchedCommandId = cmdId;
        return true;
      }
    
      this.MatchedCommandId = 0;
      return false;      
    }
    

    }

    The above class should be used when adding the commands on execution time. Here's the code that creates the commands

    public class ListMenu
    {
     private int _baselistID = (int)PkgCmdIDList.cmdidMRUList;    
     private List<IVsDataExplorerConnection> _connectionsList;
    
    
    public ListMenu(ref OleMenuCommandService mcs)
    {             
      InitMRUMenu(ref mcs);
    }    
    
    internal void InitMRUMenu(ref OleMenuCommandService mcs)
    {            
      if (mcs != null)
      {               
        //_baselistID has the guid value of the DynamicStartItem 
        CommandID dynamicItemRootId = new CommandID(GuidList.guidIDEToolbarCmdSet, _baselistID);
        DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId,      isValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);
              mcs.AddCommand(dynamicMenuCommand);                  
      }
    }
    
    private bool IsValidDynamicItem(int commandId)    
    {      
      return ((commandId - _baselistID) < connectionsCount);  // here is the place to put the criteria to add a new command to the dynamic button
    }
    
    
    private void OnInvokedDynamicItem(object sender, EventArgs args)
    {
      DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;      
    
      if (null != invokedCommand)
      {
         .....
      }
    }
    
    private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
    {
      DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;           
    
      bool isRootItem = (matchedCommand.MatchedCommandId == 0);
        matchedCommand.Enabled = true;
        matchedCommand.Visible = true;
        int indexForDisplay = (isRootItem ? 0 : (matchedCommand.MatchedCommandId - _baselistID));
        matchedCommand.Text = "Text for the command";
      matchedCommand.MatchedCommandId = 0;
    }  
    

    }

    I had to review a lot of documentation since it was not very clear how the commands can be added on execution time. So I hope this save some time whoever has to implement anything similar.