Feature Builder and Dynamic Visibility
Recently I ran into the case where I had to make a context menu item dynamic visible. In this case the menu item must only be visible when the menu is activated on the context of a configuration file, thus when the file has the ‘.config’ extension.
With VSX this would be done by handling the BeforeQueryStatus event of the OleMenuCommand. Within Feature builder the separation between the behavior and execution is applied through the concept of LaunchPoint and Command. This allows you to write all execution logic within one library, while the Feature specific items are centralized around the package project. In my opinion, this is a very powerful concept making the package clean and specific, and the library reusable for other projects, especially when applying MEF.
So now the question: the LaunchPoint is located in the package project and holds the trigger for the BeforeQueryStatus (LaunchPoint inherits from OleMenuCommand), but I do not want to write this logic in the package project but in my library project.
If you take a closer look, you can find a QueryStatusStrategy property in the LaunchPoint class(*). It returns a IQueryStatusStrategy interface. This should ring a bell: interface..strategy… DI! Alright, it is setup to make use of MEF. So we can place our logic in the library and import it in the LaunchPoint.
(*) Also a OnQueryStatus method is available and could be used, but this would not follow the separation methodology.
Let’s apply this.
IQueryStatusStrategy
This interface only has one method to implement:
public interface IQueryStatusStrategy
{
QueryStatus QueryStatus(IFeatureExtension feature);
}
where QueryStatus is a propertybag with 2 boolean properties, Enabled and Visible, and IFeatureExtension a reference to the extension calling this method
To get to the selected item we use the EnvDTE. To obtain it, import the SVsServiceProvider. Check on the extension and set the Visible property of the local created QueryStatus.
Wrapping it up results in this:
public class OnlyVisibleOnConfigQueryStrategy : IQueryStatusStrategy
{
[Import]
public SVsServiceProvider ServiceProvider { get; set; }public QueryStatus QueryStatus(IFeatureExtension feature)
{
QueryStatus queryStatus = new QueryStatus();
queryStatus.Visible = false;
queryStatus.Enabled = true;//
//check to see if item is a config file (check on extension)
//
var dte = ServiceProvider.GetService<EnvDTE.DTE>();
if (dte != null && dte.SelectedItems.Count > 0)
{
var firstItem = dte.SelectedItems.Item(1);
if (firstItem != null &&
firstItem.Name.EndsWith(".config"))
{
queryStatus.Visible = true;
}
}return queryStatus;
}
}
Now we just have to export it to the MEF. The feature package already does the importing for the library project, so no extra effort needed here to make it visible in the container.
To export, place the export attribute on the class. I use a contractname to identify the strategy:
[Export("OnlyVisibleOnConfigQueryStrategy", typeof(IQueryStatusStrategy))]
public class OnlyVisibleOnConfigQueryStrategy : IQueryStatusStrategy
LaunchPoint
We cannot apply the import on the generated class, otherwise it would be overridden when regenerated. So create a partial class in the package project and implement the import of the QueryStatusStrategy on this partial class.
The full implementation of the LaunchPoint looks like this:
public partial class VsLaunchPointDynamic : VsLaunchPoint
{
[Import("OnlyVisibleOnConfigQueryStrategy", typeof(IQueryStatusStrategy))]
protected override IQueryStatusStrategy QueryStatusStrategy
{
get { return base.QueryStatusStrategy; }
set { base.QueryStatusStrategy = value; }
}
}
Working this way, you can build a strategy library with common scenario’s.
Maybe this can have a place in the Feature builder contrib.
Saved, I enjoy your site!
Thanks a lot Jan.