Dotneteers.net
All for .net, .net for all!

DevTools Ecosystem Summit: Best Practices for Extending the IDE with a Focus on Performance, Part 1

I had the opportunity to present at the DevTools Ecocsystem Summit held in Redmond between October 19 and 23. I had two presentations, the first with the title “Best Practices for Extending the IDE with a Focus on Performance”, and the second held in cooperation with Quan To about VS 2010 extension deployment. After the presentations I decided to blog the presentations in a transcript-like way including demos with source code and screenshots.

This post is the first part of my “Performance Focus” presentation.

image 

Good Afternoon! It is great to be here at the DevTools Ecosystem Summit! Welcome to this session, just right after the lunch. I hope you’ve drunk enough coffee… I’m Istvan Novak, a C# MVP and a VSX Insider from Hungary.

I’ve spent a few years with deep diving into VSPackage development, and I would not say it is very easy, mainly because of the lack of documentation and lack of guidance.

image

It the title of the session I used the expression „Focus on Performance”. Saying this I understand three things that are my main objectives with this session:

  • First, I’d like to tell you a few interesting technologies and techniques to make you a more efficient Visual Studio Extensibility developer.
  • Second, I’d like to show you a few tricks that allow your extensions have a great performance and avoid bottleneck issues.
  • Third, I want to show you a few great potentials in Visual Studio 2010 that makes developing extensions not a challenge but rather fun.

In this session I will use only a few slides and show you a bunch of demos to let you feel what I really mean when saying technologies, techniques and tricks.

image

As you know and probably have heard quite often, the VS 2010 UI is based on the WPF technology. While VS 2010 still fully supports Windows Forms UI, I suggest you learning WPF and designing your extensions with WPF in mind.

If you have ever used macros or created VS Add-Ins, it is time to get familiar with the Visual Studio 2010 SDK that provides you two more extensibility options:

  • To possess the full power of extensibility, create Visual Studio Packages with the Managed Package Framework. With packages you can create powerful extension for VS just like if those were shipped with the product.
  • The Visual Studio SDK also opens you a new way of extending the Visual Studio editor. While in the past doing it was very painful and a hard job, with the new editor extensibility model it becomes fun. During this session, in the other room the VSX 103 session is about creating editor extensions. I suggest you downloading the session video and watching it.

Due to the COM roots of VS creating installation kits and managing deployment had many small annoying details. With VS 2010 VSPackage deployment has been significantly simplified. Visit VSX 102 in the next session slot to pick up more details on this topic.

Well, an important key to become an efficient developer: the ability to avoid the pitfalls our way is often paved with. During the session I will enumerate the most common pitfalls I’ve used to meet and help you avoiding them.

The new .NET Framework 4.0 introduces a new technology called Managed Extensibility Framework. The role of MEF is to help the shift from statically compiled applications into the dynamically composed ones. The full VS 2010 Editor Extensibility model is built on MEF, and you can also leverage on MEF’s great features. If we’ll have enough time at the end of this session I will demo you VSPackage leveraging on MEF.

Those who use the Visual Studio Automation model heavily know how painful it is to using it from C# related to macros or to dynamic languages. C# 4.0 will support extremely useful new language features that ultimately allow using COM object models in C# just like in other scripting or dynamic languages. These include the new dynamic type, optional and named parameters. In this talk I do not dive deeper in to this topics, but I’d encourage you to check what is new is C# 4.0.

image 

If you asked me what I really like in the VS 2010 release I would answer the way how the Visual Studio changes to leverage on more and more managed code. While the majority of VS 2008 user interface is unmanaged, VS 2010 UI is built on the WPF technology which is managed. The new code editor changed its UI to WPF and at the same time also the codebase went to managed code.

The WPF rendering surface is actually a DirectX surface. The performance benefits for complex user interfaces are huge. Large images can be blended and scaled in hardware and the entire scene is double-buffered to allow rich, fluid animations. So these are not HWNDs we’re dealing with – although all WPF applications will have at least one real HWND on the desktop, the contents of that HWND are managed entirely by the WPF compositing and rendering engines.

“Retained mode” is the term used to describe a graphics system where you don’t need to get involved in the painting of your window. Contrast this to HWND-based painting where you have to handle messages such as WM_PAINT and WM_ERASEBACKGND in order to get pixels to light up on the screen. Because each HWND is responsible for painting its own contents, if an application hangs or is slow to respond, this can lead to repaint problems on the desktop – holes in the display. In a retained mode system, such as WPF, you cause the scene to be rendered by adding or updating visual objects in the scene. When the visual object tree changes, then WPF will re-render the window for you.

The kinds of objects in the tree might be simple things such as rectangles or circles. They might be controls such as buttons, checkboxes or toolbars, or they might be advanced visual elements such as a video stream or a 3d model.

Data binding is simply the creation of a reference from one piece of data (a datum) to another. Typically the binding creates a connection between the UI and the application’s business logic. When the source of the binding changes, the target also updates. The “binding” is described using a “binding expression”. A binding expression may be a simple path – for example, “bind the visibility of this button to the value of this boolean property” – or it may describe complex connection between, say the results of a database query and a grid control displaying the results.

Styling allows you to override the default visualization and behavior of any WPF control. Want your buttons to glow and spin when you click them? Styling allows you to specify that behavior once and apply it to all buttons in the visual tree. Templating allows you to specify the visual appearance for all elements in a collection, for example a list box. This is a very powerful mechanism which allows us to apply a visual theme (or a “skin”) across the entire user interface by changing just the root node of our visual tree.

image

In this demo I am going to show you a simple tool window displaying process information similar to the Task Manager. I have created two separate packages that use perfectly the same logic except the first one is built with Windows Forms UI, the second with WPF. Let’s start them!

 image

The two tool windows look similar; however, the WPF is a bit more sophisticated. When clicking on the column headers we can order the table according to the column clicked, and you also can discover alternating row colorings. Of course, you can do the same effect with Windows Forms by using third-party controls, but the WPF data grid does it for you free out of the box. Try to resize the grid columns both for the top and bottom tool windows. You’ll experience that while the transition in WPF (top) window is smooth, the Windows Forms tool is flickering.

I start Spy++ and show you how windows handle are used by the Windows Forms and WPF solutions.

[Start Spy++. Use the Find Window function with the Hide Spy++ option.]

When I move the target cross over the Windows Forms implementation, you can see that it has two windows within: one for the list view representing the grid and one for the column headers. Now, when I move it over the WPF implementation, you see that the WPF tool window is nested into the main window of VS without any more HWND! By the way, with this technique you can use Spy++ to discover which parts of the new Shell have been moved to WPF and which have not!

Let’s back and see the source codes! As I mentioned earlier the two codes are identical except the user interface definitions. This is the code that populates the Windows Forms with process information. As you can see, it contains operations that make this population in the imperative way:

  1. // ------------------------------------------------------------------------
  2. /// <summary>
  3. /// Populates the list view control with process data
  4. /// </summary>
  5. // ------------------------------------------------------------------------
  6. public void RefreshData()
  7. {
  8.   ProcessListView.Items.Clear();
  9.   foreach (var processInfo in ProcessList.GetProcesses())
  10.   {
  11.     var item = new ListViewItem(
  12.       new[]
  13.         {
  14.           processInfo.Name,
  15.           processInfo.Id.ToString(),
  16.           processInfo.Priority.ToString(),
  17.           processInfo.ThreadCount.ToString(),
  18.           processInfo.Priority < 8
  19.             ? "Low (" + processInfo.Priority + ")"
  20.             : "High (" + processInfo.Priority + ")",
  21.           processInfo.WorkingSet.ToString()
  22.       }) { Tag = processInfo };
  23.     ProcessListView.Items.Add(item);
  24.   }
  25.   ProcessListView.SelectedItems.Clear();
  26.   ProcessListView.SelectedIndices.Add(0);
  27.   ProcessListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
  28.   ProcessListView.Invalidate();
  29. }

In contrast, the WPF solution does it in a declarative fashion due to the data binding feature:

  1.   // ------------------------------------------------------------------------
  2.   /// <summary>
  3.   /// Binds the grid with process data
  4.   /// </summary>
  5.   // ------------------------------------------------------------------------
  6.   public void RefreshData()
  7.   {
  8.     ProcessGrid.ItemsSource = ProcessList.GetProcesses();
  9.   }
  10. }

Here the ItemsSource property of the grid is simply assigned to the collection of process information retrieved by the GetProcesses method.

I’m sure, I do not have to tell you which one the simpler is. Let’s have a look at the codes defining the UI. For Windows Forms it is also imperative, the form’s UI is set up by C# code (code extract only):

  1. private void InitializeComponent()
  2. {
  3.   this.ProcessListView = new System.Windows.Forms.ListView();
  4.   this.NameHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  5.   this.IDHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  6.   this.ThreadsHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  7.   this.HandlesHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  8.   this.PriorityHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  9.   this.WorkingSetHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
  10.   this.SuspendLayout();
  11.   //
  12.   // ProcessListView
  13.   //
  14.   this.ProcessListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
  15.         this.NameHeader,
  16.         this.IDHeader,
  17.         this.ThreadsHeader,
  18.         this.HandlesHeader,
  19.         this.PriorityHeader,
  20.         this.WorkingSetHeader});
  21.   this.ProcessListView.Dock = System.Windows.Forms.DockStyle.Fill;
  22.   this.ProcessListView.FullRowSelect = true;
  23.   this.ProcessListView.GridLines = true;
  24.   this.ProcessListView.Location = new System.Drawing.Point(0, 0);
  25.   this.ProcessListView.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
  26.   this.ProcessListView.Name = "ProcessListView";
  27.   this.ProcessListView.Size = new System.Drawing.Size(1030, 341);
  28.   this.ProcessListView.TabIndex = 0;
  29.   this.ProcessListView.UseCompatibleStateImageBehavior = false;
  30.   this.ProcessListView.View = System.Windows.Forms.View.Details;
  31.   //
  32.   // NameHeader
  33.   //
  34.   this.NameHeader.Text = "Process Name";
  35.   this.NameHeader.Width = 200;

WPF uses XAML that is declarative (code extract only):

  1. <DataGridTextColumn Header="Process Name" Binding="{Binding Name}"
  2.                     Width="Auto" IsReadOnly="True" />
  3. <DataGridTextColumn Header="Id" Binding="{Binding Id}"
  4.                     Width="Auto" IsReadOnly="True"
  5.                     ElementStyle="{StaticResource NumericCellStyle}" />
  6. <DataGridTextColumn Header="Threads" Binding="{Binding ThreadCount}"
  7.                     Width="Auto" IsReadOnly="True"
  8.                     ElementStyle="{StaticResource NumericCellStyle}" />
  9. <DataGridTextColumn Header="Handles" Binding="{Binding Handles}"
  10.                     Width="Auto" IsReadOnly="True"
  11.                     ElementStyle="{StaticResource NumericCellStyle}" />
  12. <DataGridTextColumn Header="Priority"
  13.                     Binding="{Binding Priority, Converter={StaticResource PriorityConverter}}"
  14.                     Width="Auto" IsReadOnly="True" />
  15. <DataGridTextColumn Header="Working Set Size" Binding="{Binding WorkingSet}"
  16.                     Width="Auto" IsReadOnly="True"
  17.                     ElementStyle="{StaticResource NumericCellStyle}"
  18.                     SortDirection="Descending" />

 You see the Binding attribute of the columns that assign appropriate property of processes with the corresponding column. This XAML file declares the visual attributes of the UI, and it is very powerful. Just adding a few XAML lines without any coding we can use the styling and templating of WPF to solve tasks we use to code with Windows Forms. Here I uncomment the code that defines “mouse over” behavior for grid cells:

  1. <DataGrid.CellStyle>
  2.   <Style TargetType="DataGridCell">
  3.     <Style.Triggers>
  4.       <Trigger Property="IsMouseOver" Value="True">
  5.         <Setter Property="Background" Value="Yellow" />
  6.         <Setter Property="FontWeight" Value="Bold" />
  7.         <Setter Property="Foreground" Value="Maroon" />
  8.       </Trigger>
  9.     </Style.Triggers>
  10.   </Style>
  11. </DataGrid.CellStyle>

And also add a new column to the grid:

  1. <DataGridTextColumn Header="Window Title" Binding="{Binding WindowTitle}" Width="*" IsReadOnly="True" />

When running the modified tool window, we can recognize the effect of changes:

image

There is a new column in the grid, and as I move the mouse over the cells, the cell colors get changed.

What I showed you is just a tiny part of benefits coming from WPF but I hope it was enough to convince you to turn to WPF when developing your extensions.

Part 2>

 


Posted Oct 20 2009, 01:32 PM by inovak

Add a Comment

(required)  
(optional)
(required)  
Remember Me?