Highlighting code window text from Visual studio tool window using VSPackage - c#

I'm kind off new to the Visual Studio Extensions. Is there a way to interact to code window from Visual Studio 2010 tool window.
I have a Datagrid hosted on the VisualStudio tool window. DataGrid contains ClassName, MethodName e.tc. On the click of className/MethodName need to acheive the following
Open the particular .cs file containing className/MethodName
HighLight the particular className/MethodName.
I know this acheivable using "IWpfTextView" class, but not sure how. Did a lot of googling but in vain.Even the link below remains to be un-answered link.
Any help on the above will be greatly appreciated.

I actually did almost the same thing. You can see complete source code on Visual Localizer.
Basically you need to do two things:
Obtain IVsTextView instance for the file
call its SetSelection() method which takes range (start line, end line, start column, end column) as parameters. You may also want to call the EnsureSpanVisible() to scroll down.
Obtaining IVsTextView is quite easy as well. In my project (Visual Localizer) there's a class called DocumentViewsManager, located in VLlib/components that has fairly straightforward method called GetTextViewForFile(), which takes only file path as an argument. It does the following:
use VsShellUtilities.IsDocumentOpen method to obtain IVsWindowFrame for given file path
pass this to the VsShellUtilities.GetTextView method, which returns IVsTextView
Hope it helps.

Thanks cre8or.
I found an alternative to do the same.
You need to use "TextSelection" class to highlight the above code line.
foreach (CodeElement codeElement in projectItem.FileCodeModel.CodeElements)// search for the function to be opened
{
// get the namespace elements
if (codeElement.Kind == vsCMElement.vsCMElementNamespace)
{
foreach (CodeElement namespaceElement in codeElement.Children)
{
// get the class elements
if (namespaceElement.Kind == vsCMElement.vsCMElementClass || namespaceElement.Kind == vsCMElement.vsCMElementInterface)
{
foreach (CodeElement classElement in namespaceElement.Children)
{
try
{
// get the function elements to highlight methods in code window
if (classElement.Kind == vsCMElement.vsCMElementFunction)
{
if (!string.IsNullOrEmpty(highlightString))
{
if (classElement.Name.Equals(highlightString, StringComparison.Ordinal))
{
classElement.StartPoint.TryToShow(vsPaneShowHow.vsPaneShowTop, null);
classElement.StartPoint.TryToShow(vsPaneShowHow.vsPaneShowTop, null);
// get the text of the document
EnvDTE.TextSelection textSelection = window.Document.Selection as EnvDTE.TextSelection;
// now set the cursor to the beginning of the function
textSelection.MoveToPoint(classElement.StartPoint);
textSelection.SelectLine();
}
}
}
}
catch
{
}
}
}
}
}
}

Related

Word interop add-in - changing Window View properties do not affect Revision markup style

Using an application level add-in, I'm performing some operations on document open that require revisions (tracked changes) to be rendered inline and not hidden, so that they are contained within the Range of the document. After consulting the documentation, I thought that all I had to do was change the the view properties of the active window: MarkupMode claims to do what I want.
But this property seems to be completely disconnected from how revisions display in the document! To test this, I tried toggling the mode manually in a document, and looking at MarkupMode, and checking it immediately afterwards in an onSelectionChange event handler. I went ahead and tracked a bunch of properties of ActiveWindow.View, for good measure. To my surprise and chagrin, when I looked at the locals with changes rendered inline:
... and compared the values to those with changes hidden:
Nothing changed! What gives? Am I not looking at the right property/properties to ensure that changes are rendered inline? Is Microsoft completely incapable of writing meaningful documentation? I'll point out that I tried to make the property change in code as well, to see if the rendering of revisions would change, with no success. I would appreciate any feedback.
Edit: Simple code to duplicate issue:
private void ThisAddIn_Startup(object sender, EventArgs e)
{
Application.WindowSelectionChange += application_WindowSelectionChange;
}
private void application_WindowSelectionChange(Selection sel)
{
var testDoc = sel.Document;
var test = new
{
testDoc.ActiveWindow.View,
testDoc.ActiveWindow.View.ShowRevisionsAndComments,
testDoc.ActiveWindow.View.ShowInsertionsAndDeletions,
testDoc.ActiveWindow.View.MarkupMode,
testDoc.ActiveWindow.View.RevisionsMode
};
}
Edit 2: Beyond this contrived example, I need to have control over the markup style of Revisions because I am searching for text on DocumentOpen that may include text that is present as Revision objects. More specifically, I am attempting to do the following, using the above text (with text "powerful way to help you prove " deleted in a revision):
private void ThisAddIn_Startup(object sender, EventArgs e)
{
Application.DocumentOpen += application_DocumentOpen;
}
private void application_DocumentOpen(Document doc)
{
// expected text, as taken from screengrab example above. Includes
// text removed in a revision
string expectedText = "Video provides a powerful way to help you prove your point.";
// make sure that we're in print view
if (doc.ActiveWindow.View.Type != WdViewType.wdPrintView)
{
doc.ActiveWindow.View.Type = WdViewType.wdPrintView;
}
// attempt to ensure that document revisions are marked up inline. Does not accomplish anything
doc.ActiveWindow.View.MarkupMode = WdRevisionsMode.wdInLineRevisions;
// attempt to locate text. Will fail if revisions are not marked up inline (deletion is not part of document content range otherwise)
var locatedRange = doc.Content.OccurrenceOfText(expectedText);
}
// extension method to locate text inside a range. Searching entire Content in this example
private static Range OccurrenceOfText(this Range rng, string text)
{
rng.Find.Forward = true;
rng.Find.Format = false;
rng.Find.Execute(text);
if (!rng.Find.Found)
{
throw new Exception("Unable to locate text! Are Revisions marked up inline?");
}
// return brand new range containing located content
return rng.Document.Range(rng.Start, rng.End);
}
Edit 3: As Cindy made clear, my problem was that I was using the wrong property: I needed to use the View.RevisionsFilter.Markup property to make the change. Additionally, an issue that I hadn't diagnosed is that depending on the View properties, it is entirely possible that the Range returned from a search performed as I do returns a Text property that is different than the text that was searched with. This can happen if Revision objects are present inside the Range.
It works for me. What I did:
Created a test document. Used =rand() ->Press Enter to put some text in the document. Copied in the phrase, changing "powerful" to "useful".
Turned on track changes and made some changes, including selecting "useful" and typing "powerful".
Made sure the changes were displaying in balloons and the "Original" view of the mark-up was being shown.
Saved and closed the document.
Put a break-point on the line that calls OccurrenceOfText, then F5 to start the add-in in debug mode.
Opened the saved document. The Open event fired. I checked the values of the View.Markup and View.RevisionsView - they had changed to inline and "Final". Stepped through the rest of the code and it executed correctly (found the occurrence).
I needed to modify your code slightly since I don't have your "wrapper". I also changed the way OccurrenceOfText returns the Range: no need to do it in the complicated manner you use...
I note that you do not appear to set View.RevisionsView - perhaps this is why you're not seeing the result you expect?
private void Application_DocumentOpen(Microsoft.Office.Interop.Word.Document doc)
{
//Test revisions
// expected text, as taken from screengrab example above. Includes
// text removed in a revision
string expectedText = "Video provides a powerful way to help you prove your point.";
// make sure that we're in print view
if (doc.ActiveWindow.View.Type != Word.WdViewType.wdPrintView)
{
doc.ActiveWindow.View.Type = Word.WdViewType.wdPrintView;
}
// attempt to ensure that document revisions are marked up inline. Does not accomplish anything
Word.View vw = doc.ActiveWindow.View;
vw.MarkupMode = Word.WdRevisionsMode.wdInLineRevisions;
vw.RevisionsView = Word.WdRevisionsView.wdRevisionsViewFinal;
// attempt to locate text. Will fail if revisions are not marked up inline (deletion is not part of document content range otherwise)
var locatedRange = OccurrenceOfText(doc.Content, expectedText);
}
// extension method to locate text inside a range. Searching entire Content in this example
private static Word.Range OccurrenceOfText(Word.Range rng, string text)
{
rng.Find.Forward = true;
rng.Find.Format = false;
rng.Find.Execute(text);
if (!rng.Find.Found)
{
throw new Exception("Unable to locate text! Are Revisions marked up inline?");
}
// return brand new range containing located content
return rng; //.Document.Range(rng.Start, rng.End);
}

IVsDropdownBarClient and GetEntryText: Problems with text buffers

In my Visual Studio extension, I'm going to read the text that is in the navigation bar. Therefore I listen to window created events and from the IVsCodeWindow object I get the IVsDropdownBar to get the current selection in the dropdown bar. This works fine. Then I'm using the following code snippet to extract the text of the current selection:
string text;
barClient.GetEntryText(MembersDropdown, curSelection, out text);
if (hr == VSConstants.S_OK)
{
Debug.WriteLine("Text: " + text);
} else {
Debug.WriteLine("No text found!");
}
However, this does not work. My extension crashes with an unhandled exception in the second line of the code snippet. I read the documentation and could find the following note:
The text buffer returned in ppszText is typically created by the
IVsDropdownBarClient object and the buffer must persist for the life
of the IVsDropdownBarClient object. If you are implementing this
interface in managed code and you need to have the string disposed of
by the caller, implement the IVsCoTaskMemFreeMyStrings interface on
the IVsDropdownBarClient interface.
I assume that this is part of my problem, but I can't really understand what I have to change in my code to get it working. Any hints?
I'm pretty sure now that the Visual Studio SDK Interop DLLs have the wrong marshalling information for IVsDropDownbarClient.GetEntryText and that there's no way to call that method using that interface.
The best workaround I've found so far is:
Use the tlbimp tool to generate an alternate Interop DLL for textmgr. (You can safely ignore the dozens of warnings including the one about GetTextEntry.)
tlbimp "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\textmgr.tlb"
(Optional) If you're using source control, you'll probably want to copy the resulting file (TextManagerInternal.dll) to a subdirectory of your extension project and check it in as an external dependency.
In your Visual Studio extension project, add a reference to the file (TextManagerInternal.dll).
Add the following method that should properly handle the string marshalling.
static public string HackHackGetEntryText(IVsDropdownBarClient client, int iCombo, int iIndex)
{
TextManagerInternal.IVsDropdownBarClient hackHackClient = (TextManagerInternal.IVsDropdownBarClient) client;
string szText = null;
IntPtr ppszText = IntPtr.Zero;
try
{
ppszText = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
if(ppszText == IntPtr.Zero)
throw new Exception("Unable to allocate memory for IVsDropDownBarClient.GetTextEntry string marshalling.");
hackHackClient.GetEntryText(iCombo, iIndex, ppszText);
IntPtr pszText = Marshal.ReadIntPtr(ppszText);
szText = Marshal.PtrToStringUni(pszText);
}
finally
{
if(ppszText != IntPtr.Zero)
Marshal.FreeCoTaskMem(ppszText);
}
return szText;
}
}

Check if the current document in Visual Studio 2012 is a Code Window

I am looking for a way to have my extension check if the currently opened window in visual studio 2012 is one, where the user can write code (or any kind of text, really).
To check if the currently opened window has changed, I use
_DTE.Events.WindowEvents.WindowActivated.
This gives me the EnvDTE.Window that received the focus.
When I look at the properties of that window while debugging, and I look at EnvDTE.Window.Document.Type and it's value is "Text".
However, if I stop debugging and try to access the Document.Type property, it does not exist.
If I look for this property in the documentation of EnvDTE.Window.Document, its description says
Infrastructure. Microsoft Internal Use Only.
So now I am looking for any advice on how I could check if the currently active window is one, where I can write code (or anything else), or some other kind of document (like the solution properties for example).
Edit:
I also tried checking Window.Type and Window.Kind of the active window, but they just tell me that it's a document, not making any differentiation between a resource file, an image file or an actual source file, which is what I'm trying to find out.
Edit²:
The reason why I want to check if the current document is one where I can write code in, is because I want my extension to store information about some of those documents and I want to modify the right-click context menu based on the information I have stored, if any.
It is not a "real" answer, but you can follow status of VS GoTo command - it is available only for text editors:
bool isCodeWindow = IsCommandAvailable("Edit.GoTo");
private bool IsCommandAvailable(string commandName)
{
EnvDTE80.Commands2 commands = dte.Commands as EnvDTE80.Commands2;
if (commands == null)
return false;
EnvDTE.Command command = commands.Item(commandName, 0);
if (command == null)
return false;
return command.IsAvailable;
}
You can check to see if the document is a 'TextDocument'
bool isCodeWindow = dte.CurrentDocument.Object() is EnvDTE.TextDocument;

Using an IF statement in my SiteMaster.cs file

In my SiteMaster.cs file I want to be able to run an IF statement based on the current page the user is on.
I'm currently getting the file name using: currentPage.Text = this.Page.ToString().Substring(4, this.Page.ToString().Substring(4).Length - 5) + ".aspx"; but I'd like to then use this to run an IF statement.
It would basically be like (if currentPage == "default.aspx") { // do this }
I'm very new to .NET and taking on a existing project.
Can anyone point me in the right direction to achieve this?
Many thanks
You need to get the page name from the Request.Url.AbsoluteUri.
if(Request.Url.AbsolutePath.Contains("Default.aspx"))
Or, below will return Default.aspx
Request.Url.AbsolutePath.Substring(Request.Url.AbsolutePath.LastIndexOf('/')+1)
Its alot better to compare type. Every page is a class.
if(Page is _Default)
{
// do work
}
If you have to import a namespace and you have visual studio 2010 you can highlight _Default and holding CTRL + .

How to programmatically add a custom component to Visual Studio toolbox?

I am creating an installer for a custom SSIS component that we have written. I would like to add the custom component automatically, rather than asking the user to manually add it.
I'm trying to do this with this code:
public void AddToolboxItem(string toolboxTabName, string toolBoxItemName, string toolBoxItemPath) {
Type dteReference;
EnvDTE.ToolBox toolBox;
EnvDTE80.ToolBoxItem2 addedToolBoxItem;
// Get a reference to the visual studio development environment.
dteReference = Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
if(dteReference != null) {
// Get a reference to the toolbox of the development environment.
EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)Activator.CreateInstance(dteReference);
toolBox = (EnvDTE.ToolBox)dte.Windows.Item("{B1E99781-AB81-11D0-B683-00AA00A3EE26}").Object;
// Loop through all tab pages to find the toolbox tab page that was specified
// in the toolboxTabName parameter.
foreach (EnvDTE80.ToolBoxTab2 toolBoxTab in toolBox.ToolBoxTabs) {
// Is this the right toolbox?
if(string.Compare(toolBoxTab.Name, toolboxTabName, true) == 0) {
// First check if the component is not already in the toolbox:
bool found = false;
foreach(EnvDTE80.ToolBoxItem2 toolBoxItem in toolBoxTab.ToolBoxItems) {
if (string.Compare(toolBoxItem.Name, toolBoxItemName, true) == 0) {
found = true;
break;
}
}
// The toolbox item is not in the toolbox tab, add it:
if (!found) {
addedToolBoxItem = (EnvDTE80.ToolBoxItem2)toolBoxTab.ToolBoxItems.Add(
toolBoxItemName,
toolBoxItemPath,
EnvDTE.vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent);
}
break;
}
}
}
}
I can get a reference to the "Data Flow Transformations" ToolBox Tab, but adding an item fails without an exception or error.
I call this function with the parameters:
toolboxTabName = "Data Flow Transformations"
toolBoxItemName = "Special Test Component"
toolBoxItemPath = "c:\Program Files\Microsoft SQL Server\100\DTS\PipelineComponents\mdTest.dll"
As a sanity check, I tried using the vsToolBoxItemFormatDotNETComponent enumeration as Add()'s third parameter. This causes Add to return a valid object (ie, success), but the new item does not appear on the tool box. I suspect there is some sort of 'save' operation that may be necessary (even if I get the Add() to work properly).
Is there any way to programmatically add a SSIS component to the
toolbox?
Same question can also be found in the MSDN forums and it has been answered there by an MVP, Moderator. I am pasting the link to the answer provided in MSDN forums so that others stumbling on this question can know what the answer is.
Programmatically Add an SSIS Component to the Toolbox (social.msdn.microsoft.com)

Categories

Resources