0

You are my only hope. I have searched everywhere and just can't find anything that could help me with this one.

I've done a simple code marking plugin for Visual Studio (2010). It just finds some parts of code to highlight (by Regex), creates Spans out of the matches found and then creates Rectangle adornments for them (in the background of the text), that scroll with the text. All of this is done in view.LayoutChanged event's implementation. It works fine... but... NOT EVERY TIME! Sometimes the markers get moved by various distances (mostly up or down) and then just keep these incorrect positions while the text is scrolled. I have no idea why and WHEN this happens. I was able to discover only these few things:

  • you can reproduce this bug (move some markers from their correct positions) by dragging the vertical scrollbar of the code editor window very fast and agresively up and down (but sometimes it also fixes the positions...)
  • you cannot fix a marker's position by editing the line on which it is placed (or even the marked text)
  • you can fix the marker's position by deleting and restoring the ending "}" of the code block in which the marked code is placed (which causes the whole block of code to be reformatted)
  • the view.ViewportTop is negative when the positions are calculated incorrectly (view is a WpfTextView class) and the Geometry "g" (see below) is getting the negative Bounds.Top too. (You can test it by attaching one VS to another and set a breakpoint)

Here is the piece of my code that calculates the positions and creates the markers (LayoutChanged event):

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);

    Rect rect = new Rect(g.Bounds.Location, g.Bounds.Size);
    Rectangle marker = new Rectangle();
    marker.Margin = new Thickness(rect.X - 3, rect.Y - 2, 0, 0);
    marker.Width = rect.Width + 6; marker.Height = rect.Height + 4;
    marker.Fill = new SolidColorBrush(mark);
    marker.RadiusX = marker.RadiusY = 5;
    marker.Stroke = new SolidColorBrush(color);
    _layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, marker, null);
}

This is basically the MSDN example for creating adornments, I'm not doing any magic here.

Please help!

P.W.
  • 657
  • 8
  • 15
  • This may be a bug in Visual Studio 2010 itself, as I've seen this effect on the built-in adornments (in particular the Show Smart Tag adornment in the C# editor). – Sam Harwell Feb 21 '13 at 18:44

2 Answers2

0

I had the same problem. If you use

_layer.AddAdornment(AdornmentPositioningBehavior.TextRelative,...);

more then once you have to insert

Canvas.SetLeft(image, g.Bounds.Left);
Canvas.SetTop(image, g.Bounds.Top);

every time before.

  • 1
    As you see in my code there are mentioned lines in it. The whole block is called per each marker added. – P.W. Mar 18 '14 at 09:54
  • Also, at the moment when it gets to those lines, the `g.Bounds.Top` is also **negative** so it contains wrong value before this step already. I think it's getting it from the view (WpfTextView class), which has **the same negative value in ViewportTop**. Only reformatting the whole code block fixes it (deleting and writing the ending '}' at the end of the block that contains the lines that contains the markers). – P.W. Mar 18 '14 at 11:13
0

I've just spent the whole day on similar issue.

There're a lot of undocumented corner cases besides span moving issue. Even worse, seems-to-be-proven solution tend to break in never VS version (especially, starting with roslyn and VS2015). My favorite one was the following: the adornments were removed occasionally if you press enter multiple times inside a multiline comment. Hilarious!

So, the only working approach is the following: don't try to outsmart VS editor, it'll fool you anyway.

Instead, borrow the code from the roslyn's AdornmentManager<T>. It contains a lot of hacks I had to reinvent and even more I didn't ever suspect about, but it works. All you need to do is replace the code below

// add the visual to the adornment layer.

with yours one (the part is a good candidate to refactor into overridable method).

P.S. I know that I'm slightly late :) Hope this will save some time to another poor soul.

Sinix
  • 1,306
  • 9
  • 46