Is there a Visual Studio 2010 file preview tab plugin? - visual-studio-2010

Is there a Visual Studio 2010 plugin for the new "preview tab" feature in Visual Studio 2012?

I've tried to do it by myself, but I have no expierience in doing VS extensions nor using EnvDTE API.
I've followed Building and publishing an extension for Visual Studio 2010 to create a new Visual Studio 2010 extension.
Then I added a Tools menu item with the VSPackage Builder designer, and used this code to try to imitate the behaviour.
I am not able to:
Determine whenever a file is selected, so I have to do a loop.
Open a file in already existing window.
Change the window to be shown
at the right.
I leave the code here, just in case someone else is interested on creating an extension. Hope (s)he has a better knowledge of VS Extensibility.
[Guid(GuidList.guidPreviewDocumentTabPkgString)]
public class PreviewDocumentTabPackage : PreviewDocumentTabPackageBase
{
private DTE dte;
private Document currentTab;
protected override void Initialize()
{
base.Initialize();
this.dte = this.GetService(typeof(_DTE)) as DTE;
if (this.dte == null)
{
throw new ArgumentNullException("dte");
}
var applicationObject = (DTE2)GetGlobalService(typeof(SDTE));
var solutionExplorer = applicationObject.ToolWindows.SolutionExplorer;
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
object currentItem = null;
while (true) // To be improved
{
// Get selected items
var items = solutionExplorer.SelectedItems as Array;
// Only do logic if there is one file selected, no preview for multiple files.
if (items != null &&
items.Length == 1)
{
var item = items.GetValue(0);
if (currentItem == null)
{
currentItem = item;
}
else
{
// Only show preview if the file is "new".
if (item != currentItem)
{
currentItem = item;
// Determine if is a c# file.
var realItem = (UIHierarchyItem)currentItem;
var itemName = realItem.Name;
if (itemName.EndsWith(".cs"))
{
// Get the file
var projectItem = (ProjectItem)realItem.Object;
var projectItemPath = projectItem.Properties.Item("FullPath")
.Value.ToString();
// No already opened file.
if (currentTab == null)
{
// Open the file and get the window.
this.currentTab = this.dte.Documents.Open(projectItemPath);
}
else
{
// Todo: Open the file in the this.currentTab window.
}
}
}
}
}
// Avoid flooding
System.Threading.Thread.Sleep(100);
}
});
}
}

Related

How do you programmatically run Static Code Analysis in Visual Studio 2017?

I'm working on a solution to localize legacy applications. I've written a Visual studio add-in using EnvDte that automates the process of setting the "Localizable" flag for every form in the solution to true, which is a critical step for extracting resources on form designers. I am now trying to deal with any text that is set programmatically, text that trigger the Globalization (CA13##) warnings.
designer.Visible = true;
var host = (IDesignerHost)designer.Object;
var provider = TypeDescriptor.GetProvider(host.RootComponent);
var typeDescriptor = provider.GetExtendedTypeDescriptor(host.RootComponent);
if (typeDescriptor == null)
continue;
var propCollection = typeDescriptor.GetProperties();
var propDesc = propCollection["Localizable"];
if (propDesc != null && host.RootComponent != null &&
(bool?)propDesc.GetValue(host.RootComponent) != true)
{
try
{
propDesc.SetValue(host.RootComponent, true);
}
catch (Exception ex)
{
// log the error
}
// save changes
}
I've been able to run it manually from the menu using: Analyze -> Run Code Analysis -> On Solution to get a list of issues, but I would like to automate this step with another add-in that runs and extracts the results.
Are there any resources that point to accessing the build warnings or the results of the code analysis?
Are there any solutions that already do this using EnvDte or Roslyn?
Ok, I've managed to glean enough information to put together the add-in. Put simply, you use _dte.ExecuteCommand()
Initialize the command:
// nothing to see here...
_package = package ?? throw new ArgumentNullException(nameof(package));
var commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService == null)
return;
_dte = (DTE2)ServiceProvider.GetService(typeof(DTE));
var menuCommandId = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandId);
commandService.AddCommand(menuItem);
_events = _dte.Events.BuildEvents;
// since static code analysis is a sort of build you need to hook into OnBuildDone
_events.OnBuildDone += OnBuildDone;
Trigger the analysis
private void MenuItemCallback(object sender, EventArgs e)
{
_dte.ExecuteCommand("Build.RunCodeAnalysisonSolution");
}
Extract errors in the OnBuildDone event
private void OnBuildDone(vsBuildScope scope, vsBuildAction action)
{
Dispatcher.CurrentDispatcher.InvokeAsync(new Action(() =>
{
_dte.ExecuteCommand("View.ErrorList", " ");
var errors = _dte.ToolWindows.ErrorList.ErrorItems;
for (int i = 1; i <= errors.Count; i++)
{
ErrorItem error = errors.Item(i);
var code = error.Collection.Item(1);
var item = new
{
error.Column,
error.Description,
error.ErrorLevel,
error.FileName,
error.Line,
error.Project
};
error.Navigate(); // you can navigate to the error if you wanted to.
}
});
}

How to wait unitl the build command is over in the Visual Studio Add-In?

I want to create a Visual Studio add-in that times the build.
Here is my code so far:
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "BuildCurrentSolution.Connect.BuildCurrentSolution")
{
var window = _applicationObject.Windows.Item(Constants.vsWindowKindOutput);
var outputWindow = (OutputWindow)window.Object;
OutputWindowPane outputWindowPane = null;
for (var i = 1; i <= outputWindow.OutputWindowPanes.Count; i++)
{
if (outputWindow.OutputWindowPanes.Item(i).Name.Equals("BuildCurrentSolution", StringComparison.CurrentCultureIgnoreCase))
{
outputWindowPane = outputWindow.OutputWindowPanes.Item(i);
break;
}
}
if (outputWindowPane == null)
{
outputWindowPane = outputWindow.OutputWindowPanes.Add("BuildCurrentSolution");
}
outputWindowPane.OutputString("The build has started.\n");
var sw = new Stopwatch();
sw.Start();
_applicationObject.ExecuteCommand("Build.BuildSolution");
sw.Stop();
outputWindowPane.OutputString(string.Format("The build has ended. Elapsed time: {0} seconds.\n", sw.Elapsed.TotalSeconds));
handled = true;
}
}
}
Unfortunately, it does not do what I want it to, because _applicationObject.ExecuteCommand returns immediately.
Is it possible to wait for the completion of the command or better yet is there an event which is fired when the command is over?
Alternatively, maybe there is a different way to run the build command, the one allowing to wait or be notified when the build is over.
I was probably too eager to create this question.
Automating Visual Studio instance from separate process contains the answer. I should use _applicationObject.Solution.SolutionBuild.Build(true); instead of _applicationObject.ExecuteCommand("Build.BuildSolution");.
That's it.

How to get right click context menu on certain file types in Visual Studio

I am trying to developing an AddIn for Visual Studio to get a right click context menu for javascript files and image files. I have managed to add my Addin to the right click of all project items
What I want to achieve is to get the Addin ONLY on javascript files and image files. Something like this (Note:- currently I am getting Addin on ALL file types)
Below is the code I have in the connect
if (connectMode == ext_ConnectMode.ext_cm_UISetup)
{
object[] contextGUIDS = new object[] { };
Commands2 commands = (Commands2)_applicationObject.Commands;
string toolsMenuName = "Tools";
//Place the command on the tools menu.
//Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];
//Find the Tools command bar on the MenuBar command bar:
CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;
Microsoft.VisualStudio.CommandBars.CommandBar itemToolBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["Item"];
//This try/catch block can be duplicated if you wish to add multiple commands to be handled by your Add-in,
// just make sure you also update the QueryStatus/Exec method to include the new command names.
try
{
//Add a command to the Commands collection:
Command command = commands.AddNamedCommand2(_addInInstance, "CrmAddin", "CrmAddin", "Executes the command for CrmAddin", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
//Add a control for the command to the tools menu:
if ((command != null) && (toolsPopup != null))
{
command.AddControl(toolsPopup.CommandBar, 1);
}
if ((command != null) && (itemToolBar != null))
{
command.AddControl(itemToolBar, 1);
}
}
catch (System.ArgumentException)
{
//If we are here, then the exception is probably because a command with that name
// already exists. If so there is no need to recreate the command and we can
// safely ignore the exception.
}
I tried to filter out the file types in the QueryStatus method like this but it is of no help
if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if (commandName == "CrmAddin.Connect.CrmAddin")
{
bool supportedFileTypes = true;
foreach (Project project in _applicationObject.Solution.Projects)
{
foreach (ProjectItem projectItem in project.ProjectItems)
{
if (!projectItem.Name.EndsWith(".js"))
{
supportedFileTypes = false;
}
}
}
if (supportedFileTypes)
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
}
else
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported;
}
return;
}
}
Please help if anyone can point me to the right direction.
Just for info.
Was looking for a solution to similar problem, found this first, then after some searching I saw your question on MSDN (?)
http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/c8f35f82-c694-4a6a-8c4a-a8404a4df11f
Which gave the answer :)

Add two Command Buttons to Solution Explorer Context Menu - Visual Studion Add In

The code below should show a custom context menu item when the user right clicks on a "Project" and a different custom context menu item when the user clicks on a "Folder" in Visual Studio 2010 Solution Explorer.
The first part is working just fine - hello new Menu Item yey! - the second part when the user right clicks on Folder - isn't - it always displays the standard old context menu without the new menu item.
Any idea where I'm going wrong? It's ok to create two Command objects, right?
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
if(connectMode == ext_ConnectMode.ext_cm_UISetup)
{
object []contextGUIDS = new object[] { };
Commands2 commands = (Commands2)_applicationObject.Commands;
CommandBars cBars = (CommandBars)_applicationObject.CommandBars;
try
{
Command commandProjectSettings = commands.AddNamedCommand2(_addInInstance, "DavecProjectSchemaSettings", "Davec Project Settings", "Manages Database Project Settings", false, 1, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
Command commandAddSchemaUpdate = commands.AddNamedCommand2(_addInInstance, "DavecProjectUpdate", "Davec Update", "Updates Database Schema", false, 2, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
CommandBar vsBarProject = cBars["Project"];
CommandBar vsBarFolder = cBars["Folder"];
commandProjectSettings.AddControl(vsBarProject, 1);
commandAddSchemaUpdate.AddControl(vsBarFolder, 1);
}
catch(System.ArgumentException)
{
//ignore
}
_solutionEvents = _applicationObject.Events.SolutionEvents;
_solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SolutionEvents_Opened);
_documentEvents = _applicationObject.Events.DocumentEvents;
_documentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(_documentEvents_DocumentSaved);
}
}
I needed to include new command in the QueryStatus method, which is called when the command's availability is updated i.e.
public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
{
if(neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if (commandName == "Sappha.Davec.VSAddIn.Connect.DavecProjectSchemaSettings")
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|vsCommandStatus.vsCommandStatusEnabled;
return;
}
if (commandName == "Sappha.Davec.VSAddIn.Connect.DavecProjectUpdate")
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
return;
}
}
}

Is it possible to rename Outlook Category programmatically?

We are developing Outlook 2007 add-in. For testing outlook category renaming I've added the following code block
var session = Application.Session;
var categories = session.Categories;
var category1 = session.Categories[1];
//catefory1.Name is "Group1" before executing line below
category1.Name = "TEST!!!";
Marshal.ReleaseComObject(category1);
Marshal.ReleaseComObject(categories);
Marshal.ReleaseComObject(session);
to the end of add-in private void ThisAddIn_Startup(object sender, EventArgs e) method.
Category is renamed but if Outlook is closed, the above lines are commented, and outlook is started again - the category name is not "TEST!!!" as I expected. It is "Group1" as is was before renaming. Is it possible to rename outlook category "forever" by code? Microsoft.Office.Interop.Outlook.Category has no Save() or Update() or Persist() methods.
P.S. We are developing Outlook 2007 add-in using Visual Studio 2008, .net 3.5, C# 3.
The problem is reproduced with Outlook 2007 SP1 and SP2. Other outlook versions were not tested.
I have solved the problem (the problem itself seems to be Outlook 2007 bug) using a hack.
The following links helped me to create the hack (oops, not enough reputation to post more then 1 link):
http ://blogs.officezealot.com/legault/archive/2009/08/13/21577.aspx
http://www.officekb.com/Uwe/Forum.aspx/outlook-prog-addins/3142/Apply-Categories-to-other-users-Outlook-2007
http ://help.wugnet.com/office/set-master-category-list-Outlook-2007-ftopict1095935.html
http ://forums.slipstick.com/showthread.php?t=18189
http ://msdn.microsoft.com/en-us/library/ee203806%28EXCHG.80%29.aspx
The hack itself is show below:
using System;
using System.Text;
using System.Xml;
using System.IO;
using Microsoft.Office.Interop.Outlook;
namespace OutlookHack
{
public static class OutlookCategoryHelper
{
private const string CategoryListStorageItemIdentifier = "IPM.Configuration.CategoryList";
private const string CategoryListPropertySchemaName = #"http://schemas.microsoft.com/mapi/proptag/0x7C080102";
private const string CategoriesXmlElementNamespace = "CategoryList.xsd";
private const string XmlNamespaceAttribute = "xmlns";
private const string CategoryElement = "category";
private const string NameAttribute = "name";
public static void RenameCategory(string oldName, string newName, Application outlookApplication)
{
MAPIFolder calendarFolder = outlookApplication.Session.GetDefaultFolder(
OlDefaultFolders.olFolderCalendar);
StorageItem categoryListStorageItem = calendarFolder.GetStorage(
CategoryListStorageItemIdentifier, OlStorageIdentifierType.olIdentifyByMessageClass);
if (categoryListStorageItem != null)
{
PropertyAccessor categoryListPropertyAccessor = categoryListStorageItem.PropertyAccessor;
string schemaName = CategoryListPropertySchemaName;
try
{
// next statement raises Out of Memory error if property is too big
var xmlBytes = (byte[])categoryListPropertyAccessor.GetProperty(schemaName);
// the byte array has to be translated into a string and then the XML has to be parsed
var xmlReader = XmlReader.Create(new StringReader(Encoding.UTF8.GetString(xmlBytes)));
// xmlWriter will write new category list xml with renamed category
XmlWriterSettings settings = new XmlWriterSettings { Indent = true, IndentChars = ("\t") };
var stringWriter = new StringWriter();
var xmlWriter = XmlWriter.Create(stringWriter, settings);
xmlReader.Read(); // read xml declaration
xmlWriter.WriteNode(xmlReader, true);
xmlReader.Read(); // read categories
xmlWriter.WriteStartElement(xmlReader.Name, CategoriesXmlElementNamespace);
while (xmlReader.MoveToNextAttribute())
{
if (xmlReader.Name != XmlNamespaceAttribute) // skip namespace attr
{
xmlWriter.WriteAttributeString(xmlReader.Name, xmlReader.Value);
}
}
while (xmlReader.Read())
{
switch (xmlReader.NodeType)
{
case XmlNodeType.Element: // read category
xmlWriter.WriteStartElement(CategoryElement);
while (xmlReader.MoveToNextAttribute())
{
if ((xmlReader.Name == NameAttribute) && (xmlReader.Value == oldName))
{
xmlWriter.WriteAttributeString(NameAttribute, newName);
}
else
{
xmlWriter.WriteAttributeString(xmlReader.Name, xmlReader.Value);
}
}
xmlWriter.WriteEndElement();
break;
case XmlNodeType.EndElement: // categories ended
xmlWriter.WriteEndElement();
break;
}
}
xmlReader.Close();
xmlWriter.Close();
xmlBytes = Encoding.UTF8.GetBytes(stringWriter.ToString());
categoryListPropertyAccessor.SetProperty(schemaName, xmlBytes);
categoryListStorageItem.Save();
}
catch (OutOfMemoryException)
{
// if error is "out of memory error" then the XML blob was too big
}
}
}
}
}
This helper method must be called prior to category renaming, e.g.:
var session = Application.Session;
var categories = session.Categories;
var category1 = session.Categories[1];
//catefory1.Name is "Group1" before executing line below
OutlookCategoryHelper.RenameCategory(category1.Name, "TEST!!!", Application);
category1.Name = "TEST!!!";
Marshal.ReleaseComObject(category1);
Marshal.ReleaseComObject(categories);
Marshal.ReleaseComObject(session);
This is an Outlook bug introduces with Outlook 2007 SP2.
"Consider the following scenario. You have a custom application that can be run to create new categories in Outlook 2007.
You run the application to create a new category in Outlook 2007. Then, if you restart Outlook 2007, the category that you created is removed unexpectedly. This problem occurs after you install the Februarycumulative update or SP2."
There is a hotfix available since June 30, 2009:
http://support.microsoft.com/default.aspx/kb/970944/en
Regards,
Tim

Resources