Eyad
Eyad

Reputation: 14259

Explain to me the following VS 2010 Extension Sample code

Coders, I am building a VS 2010 extension and I am experimenting around some of the samples that came with the VS 2010 SDK.

One of the sample projects is called TextAdornment. In that project there is a weirdo class that looks like the following:

[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener

While I was experimenting with this project, I tried to debug the project to see the flow of the program and I noticed that this class gets hit when I first start the debugging.

Now my question is the following: what makes this class being the first class to get called when VS starts? In other words, why this class gets active and it runs as of some code instantiate an object of this class type?

Here is the only two files in the sample project:

TextAdornment1Factory.cs

using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;

namespace TextAdornment1
{
    #region Adornment Factory
    /// <summary>
    /// Establishes an <see cref="IAdornmentLayer"/> to place the adornment on and exports the <see cref="IWpfTextViewCreationListener"/>
    /// that instantiates the adornment on the event of a <see cref="IWpfTextView"/>'s creation
    /// </summary>
    [Export(typeof(IWpfTextViewCreationListener))]
    [ContentType("text")]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener
    {
        /// <summary>
        /// Defines the adornment layer for the adornment. This layer is ordered 
        /// after the selection layer in the Z-order
        /// </summary>
        [Export(typeof(AdornmentLayerDefinition))]
        [Name("TextAdornment1")]
        [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
        [TextViewRole(PredefinedTextViewRoles.Document)]
        public AdornmentLayerDefinition editorAdornmentLayer = null;

        /// <summary>
        /// Instantiates a TextAdornment1 manager when a textView is created.
        /// </summary>
        /// <param name="textView">The <see cref="IWpfTextView"/> upon which the adornment should be placed</param>
        public void TextViewCreated(IWpfTextView textView)
        {
            new TextAdornment1(textView);
        }
    }
    #endregion //Adornment Factory
}

TextAdornment1.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;

namespace TextAdornment1
{
    ///<summary>
    ///TextAdornment1 places red boxes behind all the "A"s in the editor window
    ///</summary>
    public class TextAdornment1
    {
        IAdornmentLayer _layer;
        IWpfTextView _view;
        Brush _brush;
        Pen _pen;

        ITextView textView;

        public TextAdornment1(IWpfTextView view)
        {
            _view = view;
            _layer = view.GetAdornmentLayer("TextAdornment1");
            textView = view;

            //Listen to any event that changes the layout (text changes, scrolling, etc)
            _view.LayoutChanged += OnLayoutChanged;
            _view.Closed += new System.EventHandler(_view_Closed);
            //selectedText();

            //Create the pen and brush to color the box behind the a's
            Brush brush = new SolidColorBrush(Color.FromArgb(0x20, 0x00, 0x00, 0xff));
            brush.Freeze();
            Brush penBrush = new SolidColorBrush(Colors.Red);
            penBrush.Freeze();
            Pen pen = new Pen(penBrush, 0.5);
            pen.Freeze();

            _brush = brush;
            _pen = pen;
        }

        void _view_Closed(object sender, System.EventArgs e)
        {
            MessageBox.Show(textView.Selection.IsEmpty.ToString());
        }

        /// <summary>
        /// On layout change add the adornment to any reformatted lines
        /// </summary>
        private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
        {
            foreach (ITextViewLine line in e.NewOrReformattedLines)
            {
                this.CreateVisuals(line);
            }
        }

        private void selectedText()
        {

        }
        /// <summary>
        /// Within the given line add the scarlet box behind the a
        /// </summary>
        private void CreateVisuals(ITextViewLine line)
        {
            //grab a reference to the lines in the current TextView 
            IWpfTextViewLineCollection textViewLines = _view.TextViewLines;
            int start = line.Start;
            int end = line.End;

            //Loop through each character, and place a box around any a 
            for (int i = start; (i < end); ++i)
            {
                if (_view.TextSnapshot[i] == 'a')
                {
                    SnapshotSpan span = new SnapshotSpan(_view.TextSnapshot, Span.FromBounds(i, i + 1));
                    Geometry g = textViewLines.GetMarkerGeometry(span);
                    if (g != null)
                    {
                        GeometryDrawing drawing = new GeometryDrawing(_brush, _pen, g);
                        drawing.Freeze();

                        DrawingImage drawingImage = new DrawingImage(drawing);
                        drawingImage.Freeze();

                        Image image = new Image();
                        image.Source = drawingImage;

                        //Align the image with the top of the bounds of the text geometry
                        Canvas.SetLeft(image, g.Bounds.Left);
                        Canvas.SetTop(image, g.Bounds.Top);

                        _layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, image, null);
                    }
                }
            }
        }
    }
}

Upvotes: 1

Views: 1909

Answers (2)

Andre H&#252;hn
Andre H&#252;hn

Reputation: 37

That is not perfectly true. Visual Studio has built in support for MEF and the Export Attribute only tells VS that this class implements IWpfTextViewCreationListener and that it can be imported by VS. In the background VS loads all packages that are installed in it and adds all MEF enabled packages to its MEF container.

When an editor is opened VS calls TextViewCreated on all imported IWpfTextViewCreationListener´s that have the corresponding ContentType set.

;)

Upvotes: 1

Sem Vanmeenen
Sem Vanmeenen

Reputation: 2151

If I understand you correctly, you want to know where the value for textview in TextViewCreated(IWpfTextView textView) comes from. As this is a sample project VS plugin that paints red boxes under all A's in the editor window, I suspect that textview is a variable that points to the editor window of VS (or some sort of object of VS itself that will apply itself to all suitable windows in VS so that all those windows have red boxes under their A's).

Now, if you debug the sample project, VS loads the plugin and will call TextViewCreated(IWpfTextView textView) to apply the plugin. Because the debugger will only stop on code in your sample project and not in code of VS itself that is the first class that is hit. It is not the first class that is being executed. That's the main class of VS.

Now, you called this a weirdo class :

[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class TextAdornment1Factory : IWpfTextViewCreationListener

so I suspect that you are not familiar with how plugin systems in VS typically work. A plugin is usually a class (or classes) with a bunch of attributes (that's what [Export(typeof(IWpfTextViewCreationListener))], [ContentType("text")] and [TextViewRole(PredefinedTextViewRoles.Document)] are). They define metadata (that is data about the class itself). They tell VS what this class is for (i.e. a plugin). For example, the Export(typeof(IWpfTextViewCreationListener) is an export attribute and tells VS that the class TextAdornment1Factory should be instantiated as a IWpfTextViewCreationListener class and not as a TextAdornment1Factory class.

If you take a look at for example WPF, attributes are practically present everywhere. I once used them myself to write a validation framework. That made it very easy and fast to add/remove validation rules and made the classes very readable because all the code that did the heavy lifting was tucked somewhere away in another class. You only saw attributes like [TextLength(min=5, max=10].

Now, if you wonder how VS reads the attributes, that happens through reflection. With reflection, code can load other as code as an object and look at it. You can for example with code figure out how many methods a class has, what the parameters are,... and, once analyzed, you can call those methods. It makes for a very powerfull concept because it allows code to analyze and rewrite itself. Or to write codes that writes code.

Upvotes: 1

Related Questions