Wednesday, February 10, 2010
Visual Studio Extensibility.
One of the thing that is usually done on a extensibility project for Visual Studio is to bind to the events that are fired by Visual Studio, like on solution opened , removed, debug begin , done, project opened, closed, projectitem added, removed.
To bind to all these events we have to get the handle to the extensibility object DTE/DTE2 . In my previous post Visual Studio CommandBars , I have explained how to get this handle. Now DTE exposes Events class that in turn have all the events that we can get. Lets look at how to do this.
Events vairable to hold on to the build events of the project opened in Visual Studio.
private BuildEvents _buildEvents;
Events vairable to hold on to the Soultion events of the solution opened in Visual Studio.
private SolutionEvents _solutionEvents;
Events vairable to hold on to the project events of the project opened in Visual Studio.
//For CS project
private ProjectItemsEvents _projectItemEventsCs;
//For VB project
private ProjectItemsEvents _projectItemEventsVb;
Events vairable to hold on to the DTE events of the dte object.
private DTEEvents _dteEvents;
Getting the events is simple:-
//Getting the build event of the project.
_buildEvents = _dte.Events.BuildEvents;
//Binding the build events.
//On build Begin
_buildEvents.OnBuildBegin += buildEvents_OnBuildBegin;
//On build Done.
_buildEvents.OnBuildDone += buildEvents_OnBuildDone;
//Getting the solution events
_solutionEvents = _dte.Events.SolutionEvents;
//Binding the solution events.
//Project added to the solution
_solutionEvents.ProjectAdded += solutionEvents_ProjectAdded;
//Solution opened in the VS.
_solutionEvents.Opened += solutionEvents_Opened;
//Getting the DTE events.
_dteEvents = _dte.Events.DTEEvents;
//On bigining the shutdown of solution
_dteEvents.OnBeginShutdown += DteEvents_OnBeginShutdown;
//Project Items Events
// Getting the events for the CS project
_projectItemEventsCs = _dte.Events.GetObject("VBProjectItemsEvents") as ProjectItemsEvents;
// Getting the events for the VB project
_projectItemEventsVb = _dte.Events.GetObject("CSharpProjectItemsEvents") as ProjectItemsEvents;
//On project item removed from the project. (So are the event for ItemAdded, but no event for the change in the project item)
_projectItemEventsCs.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(_projectItemEventsCs_ItemRemoved);
_projectItemEventsVb.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(_projectItemEventsCs_ItemRemoved);
These were the ways to bind to the Visual Studio Solution and Project events. Now lets look at something to access and set the properties of the solution and projects .
To access the assembly name of the project:-
string assemblyName = _dte.Solution.Projects.Item(1).Properties.Item("AssemblyName").Value.ToString();
To set the assembly name:-
_dte.Solution.Projects.Item(1).Properties.Item("AssemblyName").Value = assemblyName;
To get the output path of the Project:-
string OutputPath = _dte.Solution.Projects.Item(1).ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
To build the solution :-
_dte.Solution.SolutionBuild.Build(true);
To do a Debug build and Release Build for the project in Solution.
_dte.Solution.SolutionBuild.BuildProject("Debug", _dte.Solution.Projects.Item(1).FullName, true);
_dte.Solution.SolutionBuild.BuildProject("Release", _dte.Solution.Projects.Item(1).FullName, true);
To animate the Visual Studio Status Bar:-
_dte.StatusBar.Animate(true, vsStatusAnimation.vsStatusAnimationSave);
//To set value for the progress
_dte.StatusBar.Progress(false, "Done", 100, 100);
//To stop animation.
_dte.StatusBar.Animate(false, vsStatusAnimation.vsStatusAnimationSave);
To check the type of the Project :-
if (_dte.Solution.Projects.Item(1).Kind == PrjKind.prjKindVBProject)
{
projectType = ".vbproj";
}
else
{
projectType = ".csproj";
}
To load controls assembly into the ToolBox:-
Window toolBoxWindow = _dte.Windows.Item(Constants.vsWindowKindToolbox);
toolBoxWindow.Visible = true;
ToolBoxTabs tabs = ((ToolBox)toolBoxWindow.Object).ToolBoxTabs;
ToolBoxTab waspTab = tabs.Add("My tools");
_dte.ExecuteCommand("View.PropertiesWindow", "");
waspTab.Activate();
waspTab.ToolBoxItems.Item(1).Select();
waspTab.ToolBoxItems.Add("Controls", ControlsFilePath, vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent);
Hope all the beings will find it useful while dealing with the VS extensibility.
Sunday, January 24, 2010
Visual Studio CommandBars and Commands
This blog is about Visual Studio Extensibility. Recently I was at the task of providing my own menuitems in the Visual Studio and catching the events generated by default Visual Studio toolbar buttons.
In Visual Studio Extensibility api DTE / DTE2 are the application objects through which we can access the Visual Studio CommandBars like Tools,File, Edit etc. Add-In,Vba, Vsta developers are probably familar with the DTE/DTE2 application object. DTE not only provide access to the commandbars but whole set of Visual Studio features.
We can access the solution opened in the VS, we can access projects in the solution and there properties.We can set debug paths, reference paths , project types and all lots of thing.
But this blog is about CommandBars and Commands. CommandBar object is the main menu that is in VS like Tools, Debug, File etc, then there is CommandBar Controls and CommandBar buttons which are the menuitem for the main menu. To access the CommandBars we should have the application object DTE. It is provided as a parameter (object application) in OnConnection method of the Add-In project.
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
}
IDTEProvider provider = (IDTEProvider)new VSTADTEProviderClass();
EnvDTE.DTE _dte = (EnvDTE.DTE)provider.GetDTE("HostName", 0);
Now we have our DTE object , we can start accessing the CommandBars of Visual Studio. DTE object exposes the CommandBars property that contains the list of all avialable Command Bars within the VS. Lets get the Tools Command Bar.
CommandBars commandBars = this._dte.CommandBars as CommandBars;
CommandBar toolsMenuBar = ((CommandBars)this._dte.CommandBars)["Tools"];
Adding the Command Bar button to the Tools CommandBar is simple , just call the CommandBar.Controls.Add method with right parameters, this will return CommandBarControl object and now cast the CommandBarControl in the CommandBarButton. CommandBarButton have many properties that we can set, some of useful ones are set in the following code.
Where to set the CommandBar Button, is it temporary, its type (PopUp, DropDown) all can be set by the parameters passed on to the Add method.
CommandBarControl myControl = toolsMenuBar.Controls.Add (MsoControlType.msoControlButton, 1, "", 4, true);
CommandBarButton myButton = reloadControls as CommandBarButton;
myButton ols.Caption = "My Button"; // Menuitem name
myButton .Visible = true; //Visible property
myButton .BeginGroup = true; // Gets the seperator for menuitem
myButton .FaceId = 0319; // icon to show
myButton .Enabled = false; // Enabled property.
Now we can bind a event handler to this custom CommandBar Button and perform our custom action.
myButton.Click += new _CommandBarButtonEvents_ClickEventHandler(myButton_Click);
The default buttons of VS, on there click perform certain actions like opening New Project Dialog, Open Dialog etc. These actions are invoked by the Visual Studio Commands which are in turn invoked by click event of CommandBarButtons.
Commands are not the UI elements but way by which VS execute particular action.
To get the events of these commands we should know the GUID and ID of the VS Commands.
EnvDTE.CommandEvents newProjectEvents = this._dte.Events.get_CommandEvents("{5EFC7975-14BC-11CF-9B2B-00AA00573819}", 216);
How to get these Guid's and Id's ?. We can loop through DTE.Commands collection to find our Command and Guid,Id associated with it.
internal void FindGuid()
{
string commandName = String.Empty;
string commandGuidNewProject = String.Empty;
string commandGuidOpenProject = String.Empty;
int commandIdOpenProject = 0;
int commandIdNewProject = 0;
EnvDTE.Command command;
for(int i = 1; i < m_dte.Commands.Count; i++)
{
command = m_dte.Commands.Item(i, i);
commandName = command.Name;
if(commandName == "File.NewProject")
{
commandGuidNewProject = command.Guid.ToString();
commandIdNewProject = command.ID;
}
if(commandName == "File.OpenProject")
{
commandGuidOpenProject = command.Guid.ToString();
commandIdOpenProject = command.ID;
}
}
}
The CommandEvents that we accessed through the Guid /Id of VS command have many events with which we can bind our event handlers. Events which we are interested in are AfterExecute and BeforeExecute, these are fired after and prior to the execution of the Command action.
newProjectEvents.BeforeExecute+= new _dispCommandEvents_BeforeExecuteEventHandler(newProjectEvents_BeforeExecute);
newProjectEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(newProjectEvents_AfterExecute);
We have seen how to add our cutom command bar button , bind its click event and how to bind to the events of VS Commands.Hope you will find something useful out of this blog.
Friday, January 15, 2010
Creating Project Template in Visual Studio
In this post i will be showing steps to make it happen.
Staring point for creating our project template is VSDIR and VSZ files .VSDIR is a text file that tells the New Project Dialog box how to display our project template , its display name,order in dialog box and the icon associated with it.VSZ files define the wizard class to invoke for logic and parameters to provide to the wizard class.Relative path of these vsz files are defined in the VSDIR file along with other parameters.
Lets look at the conent of VSDIR file:-
Template.vsz|{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}|My Template|70|Template CS|D:\Helper Projects\Helper Projects\SatelliteAssembly\IconResourceDll.dll|102||
All the parameters are seperated by pipe (|).
- First parameter define the vsz file , in this case it is in the same folder as the VSDIR file.
- Next is the product GUID (vc++,c# etc) containing localized resources for the product.
- Display name of the template as it will be shown in New Project Dialog.
- Sort priority , 1 being the highest.(Template be shown next to all the template with same priority)
- Discription of the Template.
- Path of the associated Icon resource dll.
- Icon resource Id.
- Flags
- Suggested Base name.
Lets see VSZ file.
VSWIZARD 7.0
Wizard={BAA02C12-00F5-4e74-A6AF-AEC6930AC067}
Param="TemplateCS"
First parameter in VSZ file defines the Visual Studio Enviroment template version, next is the guid of the wizard class containg the logic for opening the template, then the parameters to pass to the wizard class.
Now after creating these files we have to put them in specific location , the path is
[WinDrive]:\Program Files\Microsoft Visual Studio 10.0\VC#\CSharpProjects
OR
[WinDrive]::\Program Files\Microsoft Visual Studio 10.0\VB\CSharpProjects
Now if we check by opening the visual studio and opening the new project dialog box , we will find our template there. Next step is to create the wizard class that will handle our project opening logic , remember we have put the guid of this class in the VSZ file. Creating the wizard class start's with adding reference to the Envdte from Envdte.dll that contains the IDTWizard interface .Create a new class library project and define your wizard class.Now we add envdte namespace to our class and implement the IDTWizard .IDTWizard is the interface that makes our class a wizard.There is single method to implement Execute, this is the method that is called when our template is invoked from the New Project Dialog box .
[ComVisible(true)]
[Guid("BAA02C12-00F5-4e74-A6AF-AEC6930AC067")]
public class TemplateWizard : IDTWizard
{
[ComVisible(true)]
public void Execute(object Application, int hwndOwner, ref object[] ContextParams, ref object[] CustomParams, ref wizardResult retval)
{
}
}
We have marked our class ComVisible so that com clients can access our managed class , we have also defined the guid attribute with a guid that we have referenced in the VSZ file.This guid is the way how the VS know which class to call when the template is selected.
Execute method have parameters that define :-
- The Visual Studio Automation Model object .(DTE)
- Parent handle of the wizard class.
- Context parameters array .
- Custom parameters array.The array of the parameters that we have defined in the vsz file.
We can use the EnvDte.Dte object passed on as parameter to open our project (.csproj / .vbproj)in VS . Method provided by the DTE is AddFromTemplate that takes on parameters
DTE.Solution.AddFromTemplate( param1 , param1,param1 ,param1 )
- Filename and full path of the template file with extension .(vstemplate file)
- Destination path where to create this project.
- Project Name
- Bool describing whether to open the project in current or new solution.
We can also define logic to find out that if the project already happens to be at destination path and open that project instead of creating new one.
DTE.Solution.AddFromFile(openProjectPath, true);
Now before adding our project to the VS we can define whole set of logic to happen.Most comman of all is altering our class files in project that we are about to open. We can define namespace , class names and all other kind of custom code depending on certain criteria prior to opening the project in VS . We can do a lot of things based on our project need , like call some service, open a dilog box (WPF), add references to project , load controls to the toolbox in VS etc.
After we have created our wizard class , we have to register the assembly so that VS could locate our class when our template is invoked from the New Project Dialog. The way to do this is through Regasm utility with codebase option.
:> Regasm /codebase [path of our wizard dll]
After our type is registered we are all set to create new project from our project template.