How do I intercept the file paste event in my Visual Studio extension?
File paste = pasting the whole file in Solution Explorer.
My goal is to apply certain replacements to the copied file.
It can be intercepted using DTE.Events.CommandEvents (MSDN) with typeof(VSConstants.VSStd97CmdID).GUID as command guid and VSConstants.VSStd97CmdID.Paste as command id.
Example:
protected override void Initialize() {
var dte = (DTE)GetService(typeof(DTE));
var pasteGuid = typeof(VSConstants.VSStd97CmdID).GUID.ToString("B");
var pasteID = (int)VSConstants.VSStd97CmdID.Paste;
_pasteEvent = dte.Events.CommandEvents[pasteGuid, pasteID];
_pasteEvent.BeforeExecute += delegate { Trace.WriteLine("Before paste."); };
_pasteEvent.AfterExecute += delegate { Trace.WriteLine("After paste."); };
}
This is not really perfect as it may intercept paste in other contexts as well, but it a good first step. It is also possible to watch ItemAdded event during the paste to get the pasted items.
Related
I'm developing a Visual Studio extension in which one of the implemented commands needs to be available only when the active document is a text document (like e.g. the Visual Studio's "Toggle Bookmark" does). The problem is that I can't figure out how to tell when that's the case.
Right now I have a half working solution. In the package's Initialize method I subscribe to DTE's WindowActivated event, and then whenever a window is activated I check if the window DocumentData property is of type TextDocument:
protected override void Initialize()
{
base.Initialize();
var dte = Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
dte.Events.WindowEvents.WindowActivated += WindowEventsOnWindowActivated;
//More initialization here...
}
//This is checked from command's BeforeQueryStatus
public bool ActiveDocumentIsText { get; private set; } = false;
private void WindowEventsOnWindowActivated(Window gotFocus, Window lostFocus)
{
if (gotFocus.Kind != "Document")
return; //It's not a document (e.g. it's a tool window)
TextDocument textDoc = gotFocus.DocumentData as TextDocument;
ActiveDocumentIsText = textDoc != null;
}
The problem with this approach is that 1) Window.DocumentData is documented as ".NET Framework internal use only", and 2) this gives a false positive when a document that has both a code view and a design view (e.g. a .visxmanifest file) is open in design mode.
I have tried to use IVsTextManager.GetActiveView as well, but this is returning the last active text view opened - so if I open a .txt file and then a .png file, it returns data for the .txt file even if it's not the active document anymore.
So, how do I check if the active document is a text document, or the code view of a document that can have a designer... and if possible, not using "undocumented" classes/members?
UPDATE: I found a slightly better solution. Inside the window activated handler:
ActiveDocumentIsText = gotFocus.Document.Object("TextDocument") != null;
At least this one is properly documented, but I still have the problem of false positives with designers.
I finally got it. It's somewhat tricky, but it works and is 100% "legal". Here's the recipe:
1- Make the package class implement IVsRunningDocTableEvents. Make all the methods just return VSConstants.S_OK;
2- Add the following field and the following auxiliary method to the package class:
private IVsRunningDocumentTable runningDocumentTable;
private bool DocIsOpenInLogicalView(string path, Guid logicalView, out IVsWindowFrame windowFrame)
{
return VsShellUtilities.IsDocumentOpen(
this,
path,
VSConstants.LOGVIEWID_TextView,
out var dummyHierarchy2, out var dummyItemId2,
out windowFrame);
}
3- Add the following to the Initialize method of the package class:
runningDocumentTable = GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
runningDocumentTable.AdviseRunningDocTableEvents(this, out var dummyCookie);
4- Don't blink, here comes the magic! Implement the IVsRunningDocTableEvents.OnBeforeDocumentWindowShow method as follows:
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
runningDocumentTable.GetDocumentInfo(docCookie,
out var dummyFlags, out var dummyReadLocks, out var dummyEditLocks,
out string path,
out var dummyHierarchy, out var dummyItemId, out var dummyData);
IVsWindowFrame windowFrameForTextView;
var docIsOpenInTextView =
DocIsOpenInLogicalView(path, VSConstants.LOGVIEWID_Code, out windowFrameForTextView) ||
DocIsOpenInLogicalView(path, VSConstants.LOGVIEWID_TextView, out windowFrameForTextView);
//Is the document open in the code/text view,
//AND the window for that view is the one that has been just activated?
ActiveDocumentIsText = docIsOpenInTextView && pFrame == logicalViewWindowFrame;
return VSConstants.S_OK;
}
With Firefox 17.0.1 I am using an add-on called KeyConfig 20110522 to set some new hot keys and also set the acceltext of menuitems for my new keys as well as for add-ons that do not bother to do so.
I want the acceltext of the menuitems to be set when Firefox starts, but currently I am just using a hot key to execute the following code against the UI via KeyConfig:
document.getElementById("tabmix-menu")
.setAttribute("acceltext","Alt+Ctrl+Shift+T");
// more of the same...
I need a couple of beginners tips:
How can I execute arbitrary code against the UI in the same way as I execute against an HTML page via the console?
Is there a sneaky way to get a clump of code to execute on browser start-up without delving into XUL development?
Is there a way to trace commands executed against the UI so I can get at command calls instead of using triggers when I set my hot keys like so:
document.getElementById("tabmix-menu").click();
Any other tips on this type of low-level hacking would also be welcome.
You can execute arbitrary code against the Firefox UI from an addon, but as you say, doing all the XUL related stuff is a bit boring :-)
Enter "Bootstrapped" extensions!
Part 1:
A "Bootstrapped" (or re-startless) extension needs only an install.rdf file to identify the addon, and a bootstrap.js file to implement the bootstrap interface.
Bootstrapped Extension: https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions
Good example: http://blog.fpmurphy.com/2011/02/firefox-4-restartless-add-ons.html
The bootstrap interface can be implemented very simply:
function install() {}
function uninstall() {}
function shutdown(data, reason) {}
function startup(data, reason) { /* YOUR ARBITRARY CODE HERE! */ }
You compile the extension by putting install.rdf and bootstrap.js into the top-level of a new zip file, and rename the zip file extension to .xpi.
Part 2:
Your code is privileged and can use any of the Mozilla platform APIs. There is however an issue of timing. The moment-in-time at which the "startup" function is executed is one at which no Chrome window objects exist yet!
If it's important for your code that you have a Chrome Window, we need to wait for it to appear:
// useful services.
Cu.import("resource://gre/modules/Services.jsm");
var loader = Cc["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
var wmSvc = Cc["#mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var logSvc = Cc["#mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
// get the first gBrowser
var done_startup = 0;
var windowListener;
function do_startup(win) {
if (done_startup) return;
done_startup = 1;
wmSvc.removeListener(windowListener);
var browserEnum = wmSvc.getEnumerator("navigator:browser");
var browserWin = browserEnum.getNext();
var tabbrowser = browserWin.gBrowser;
/* your code goes here! */
}
// window listener implementation
windowListener = {
onWindowTitleChange: function(aWindow, aTitle) {},
onCloseWindow: function(aWindow) {},
onOpenWindow: function(aWindow) {
var win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
win.addEventListener("load", function(aEvent) {
win.removeEventListener("load", arguments.callee, false);
if (aEvent.originalTarget.nodeName != "#document") return;
do_startup();
}
};
// CODE ENTRY POINT (put this in bootstrap "startup" function)
wmSvc.addListener(windowListener);
I have some T4 templates in my project. Whenever I make changes and save the tt file, it auto update the generated files. This is a template that loops all tables in a database and generates about 100+ files. So visual studio hangs for a few seconds every time I save my template and this is annoying. Is there a way to disable to "auto-refresh" function and I can manually run the template through the context menu.
Thanks!
You could delete TextTemplatingFileGenerator under "Custom Tool" in the file's Properties while you are editing it, and then put it back when you are finished.
I had a similiar issue. I found a quick work around by creating a ttinclude file (actually this was already a standard include file containing utility functions for my templates) and including it in all of my T4 templates. Then I simply created a compiler error in the include file. Thus when the generator attempted to run it would simply fail on the compile. Then when I'm ready to actually generate, I get rid of the offending code and then generate.
e.g. To cause a failure:
<#+
#
#>
To disable the failure:
<#+
//#
#>
You can also use this trick in the T4 template itself if you just want to disable the one you're working on.
Hopefully future VS versions will allow you to simply disable the auto-transform.
Since the TT is always executed (still), I found a different way to control the output when the TT is executed.
/********SET THIS TO REGENERATE THE FILE (OR NOT) ********/
var _RegenerateFile = true;
/********COS VS ALWAYS REGENERATES ON SAVE ***************/
// Also, T4VSHostProcess.exe may lock files.
// Kill it from task manager if you get "cannot copy file in use by another process"
var _CurrentFolder = new FileInfo(Host.ResolvePath(Host.TemplateFile)).DirectoryName;
var _AssemblyLoadFolder = Path.Combine(_CurrentFolder, "bin\\Debug");
Directory.SetCurrentDirectory(_CurrentFolder);
Debug.WriteLine($"Using working folder {_CurrentFolder}");
if (_RegenerateFile == false)
{
Debug.WriteLine($"Not Regenerating File");
var existingFileName = Path.ChangeExtension(Host.TemplateFile, "cs");
var fileContent = File.ReadAllText(existingFileName);
return fileContent;
}
Debug.WriteLine($"Regenerating File"); //put the rest of your usual template
Another way (what I eventually settled on) is based on reading a conditional compilation symbol that sets a property on one of the the classes that is providing the data for the T4. This gives the benefit of skipping all that preparation (and IDE lag) unless you add the REGEN_CODE_FILES conditional compilation symbol. (I guess this could also be made into a new solution configuration too. yes, this does work and removes the need for the class change below)
An example of the class i am calling in the same assembly..
public class MetadataProvider
{
public bool RegenCodeFile { get; set; }
public MetadataProvider()
{
#if REGEN_CODE_FILES
RegenCodeFile = true; //try to get this to set the property
#endif
if (RegenCodeFile == false)
{
return;
}
//code that does some degree of preparation and c...
}
}
In the TT file...
var _MetaProvider = new MetadataProvider();
var _RegenerateFile = _MetaProvider.RegenCodeFile;
// T4VSHostProcess.exe may lock files.
// Kill it from task manager if you get "cannot copy file in use by another process"
var _CurrentFolder = new FileInfo(Host.ResolvePath(Host.TemplateFile)).DirectoryName;
var _AssemblyLoadFolder = Path.Combine(_CurrentFolder, "bin\\Debug");
Directory.SetCurrentDirectory(_CurrentFolder);
Debug.WriteLine($"Using working folder {_CurrentFolder}");
if (_RegenerateFile == false)
{
Debug.WriteLine($"Not Regenerating File");
var existingFileName = Path.ChangeExtension(Host.TemplateFile, "cs");
var fileContent = File.ReadAllText(existingFileName);
return fileContent;
}
Debug.WriteLine($"Regenerating File");
NuGet package restore does not play well with the PostSharp package, this is of great annoyance to me and manually editing the csproj files is quickly becoming old.
I want to make a small Visual Studio AddIn that fixes the project for me by adding an extra button to the unloaded project context menu.
The problem I am facing sounds simple but haunts me: I somehow can't locate the CommandBar for the unloaded project context menu.
Thus, summarizing the question: What is the CommandBar name for the unloaded project context menu.
Many thanks!
Apparantly: "Stub Project". Found in the list at: CommandBar names
To sum up the OnConnection method to register this:
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;
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];
CommandBars cmdBars = (CommandBars)(_applicationObject.CommandBars);
CommandBar vsBarProject = cmdBars["Stub Project"];
CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;
//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, "FixNuGetPostSharp", "Fix NuGet PostSharp", "Executes the command for VsextensionTest", true, 0, 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(vsBarProject);
}
}
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 have a project which contains multiple forms that have a microsoft visio activex control in them.
For some reason they keep interfering with one another. Here's a snip it of my code, simplified:
private void OpenRDM()
{
if (RDMform == null)
{
// Constructor opens a new form, and populates a Visio control on it
RDMform = new RDM.MainForm();
}
RDMform.Show();
}
private void OpenPSM()
{
if (PSMform == null)
{
// Constructor opens a new form, and populates a Visio control on it
PSMform = new PSM.MainForm();
}
PSMform.Show(); // Problem occurs here, PSM Visio control gains contents of RDM's Visio Control
}
public void main()
{
OpenRDM();
RDMform.Hide();
OpenPSM();
PSMForm.Hide();
}
I can call one of these functions and it works fine, but if I hide the form, and then call the other one, its Visio control loads with the contents of the first form, instead of the correct new stuff. I traced it, and the second Visio control is populated just fine, until the second Show() method is called, at which point it mysteriously goes back to the contents of the first Visio control.
I can't figure out what is going on here, Visio seems to think that they are the same control.
Edit*
In response to comment about setting the .src property. The source is a behemoth, so I can't reasonably post everything it does. But here's those lines.
in RDM:
If visioControl.Src <> TemplateFullName Then
visioControl.Src = TemplateFullName
Else
visioControl.Src = ""
visioControl.Src = TemplateFullName
End If
in PSM:
drawingControl.Src = string.Empty;
drawingControl.Src = vstFilepath;
InitializeFlowDiagram(dbFilepath); // updates shapes from database values
Both modules actually use the same visio diagram including page names, they just display it in different ways. So I'd like to be able to back and forth between them without having to reload everything. Maybe the activeWindow is involved in the problem, I'll have to do more research.