Tutorial Table of Contents:
- Part 1: Introduction to VS extensibility
- Part 2: Add Menu Item
- Part 3: Add command to context menu and get selected code
- Part 5: Highlight code in Editor
- part 6: Follow a span of code with TrackingSpan
- Part 7: Insert buttons between text characters with Adornments
- Part 8: Add UI in the indicator margin with Glyphs
During the tutorial we are going to build a VS extensions called CodyDocs and place it on GitHub . Each tutorial part is a standalone tutorial on a specific topic and can be viewed individually. CodyDocs will save code documentation in a separate file and the extension will allow to view and edit the documentation in the editor itself.
Part 4: Show a popup Window
In the previous part we created a Command that appears in the code editor’s context menu.
Let’s place our logic to run on our command’s callback:
private void MenuItemCallback(object sender, EventArgs e)
{
TextViewSelection selection = GetSelection(ServiceProvider);
string activeDocumentPath = GetActiveDocumentFilePath(ServiceProvider);
ShowAddDocumentationWindow(activeDocumentPath, selection);
}
ShowAddDocumentationWindow is the only line relevant to this part of the tutorial.
To show our own custom popup window, Microsoft recommends using DialogWindow , which is what we will do. This is a WPF window which allows you to act as a modal dialog. Which means the popup blocks the rest of Visual Studio UI until closed.
First, to use WPF we will need these references:
- PresentationCore
- PresentationFramework
- WindowsBase
- System.Xaml
System.Xaml is probably the only thing missing from the regular VSIX template project. To add it, right click on the project -> Add -> Reference and choose System.Xaml.
Create a DialogWindow
For some reason, we can’t add a new DialogWindow item to our project directly. It doesn’t exist in the New Item dialog. There’s an easy way to get over this obstacle though.
First, we need to create a BaseDialogWindow class we can use for all our popup windows:
public class BaseDialogWindow : DialogWindow
{
public BaseDialogWindow()
{
this.HasMaximizeButton = true;
this.HasMinimizeButton = true;
}
}
Now we need to create our custom window with XAML and code behind that we can edit. There’s a little trick we need to use so we can have our .xaml file next to our .xaml.cs file. First, add a new UserControl item to the project. Let’s call it AddDocumentationWindow. We’re calling it a Window because it will become that real soon.
The user control initially looks like this:
<UserControl x:Class="CodyDocs.AddDocumentationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...>
<TextBlock Text="Hello World!/>
</UserControl>
In .xaml.cs:
public partial class AddDocumentationWindow: UserControl
{
public AddDocumentationWindow()
{
InitializeComponent();
}
}
Now let’s change it to a Window. It should inherit BaseDialogWindow, so in .xaml, UserControl should change to BaseDialogWindow:
<local:BaseDialogWindow x:Class="CodyDocs.AddDocumentationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...>
<TextBlock Text="Hello World!/>
</UserControl>
The .xaml.cs file remains unchanged.
That’s it. Now you can edit the XAML and code behind as needed.
Show the popup Dialog Window
To show the dialog, create an instance of the Dialog Window and call ShowDialog:
private void ShowAddDocumentationWindow()
{
var documentationControl = new AddDocumentationWindow();
documentationControl.ShowDialog();
}
Popup window in CodyDocs
As mentioned before, we’re building an extension called CodyDocs as part of this tutorial. I added code that shows a popup window from our context menu command “Add documentation”. Here’s how it looks:
The code is available on [GitHub](https://github.com/michaelscodingspot/CodyDocs). To checkout to the extension code right after adding the popup window, use the Tag **Part4**:<pre class="theme:powershell lang:default decode:true">> git clone https://github.com/michaelscodingspot/CodyDocs.git
> git checkout tags/Part4
Summary
In this part, we saw how to show a popup dialog window with a Visual Studio extension. I’ll admit it’s not the most intuitive thing in the world, but not too hard either.
In following tutorial parts, we’ll see how to show adorners (heads up display) in the code editor. A bit further on, I also plan to get into Roslyn, the C# compiler that allows us to analyze and rewrite code.
Great post!
Hi,
I run in to the same problem as Pascal. I use VS Community 2017 v 15.7.3
Learned a lot from this tutorial and a good place to start coding extensions. However the show pop section doesnt work in my version of VS 2017, version 15.5.4. I get invalid markup in the xaml and in the error list i get... The name "BaseDialogWindow" does not exist in the namespace "clr-namespace:CodyDocs". Unless im missing something!
Hello Dennis,
I had the same issue, they were both in namespace CodyDocs, but changing BaseDialogWindow's namespace to a different namespace fixed it
Just change it to for example "Dialog"
Then in AddDocumentationWindow.xaml add namespace reference inside opening tag of the user control " xmlns:base="clr-namespace:Dialog" "
And finally redefine wpf control as
Hope this fixes it for you!
Hi Dennis.
The namespace of your BaseDialogWindow.cs file is probably not CodyDocs. Try changing the namespace to CodyDocs. Or alternatively, change the namespace in XAML to the same namespace as your BaseDialogWindow.
You can actually just use DialogWindow.
<ui:DialogWindow ...
xmlns:ui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.14.0"
Change the version as required.
Hey,
Great tutorial, but I always get an exception in the member fucntion GetSelection.
The textManager is null and when it tries to get the activeView there's an exception.
I even tried with your code from Github and it has the same problem.
Task service = serviceProvider.GetServiceAsync( typeof ( SVsTextManager ) );
IVsTextManager2 textManager = service as IVsTextManager2;
IVsTextView view;
int result = textManager.GetActiveView2(1, null, (uint)_VIEWFRAMETYPE.vftCodeWindow, out view );
Hi,
I ran in to the same issue. Since the methods are Async you'll have to cast the .Result:
var textManager = service.Result as IVsTextManager2;
I'm trying to create a popup window for a different extension using this method. I'm getting an error of " The name "BaseDialogWindow" does not exist in the namespace "clr-namespace:ProjectName.Dialogs" i've changed everything to use the same namespace yet im still getting the error. Any idea why?
You must have missed something. Maybe you didn't change in the XAML?
I suggest some corrections:
On VS 2017, the 4 DLL have to be referenced.
As VS complains, we have to change it to: "public partial class AddDocumentationWindow : BaseDialogWindow"
Example:
private void ShowAddDocumentationWindow(string docPath, TextViewSelection selection)
{
var documentationControl = new AddDocumentationWindow();
documentationControl.SourceFile.Content = docPath;
documentationControl.Selection.Content =
string.Format("From ({0},{1}) to ({2},{3})",selection.StartPosition.Column, selection.StartPosition.Line, selection.EndPosition.Column, selection.EndPosition.Line);
documentationControl.ShowDialog();
}
Example:
Source file:
Selection: