How To Create a Custom Annotation

The following step-by-step tutorial demonstrates how to create a custom annotation using Visual Studio 2010. The download link for the complete source code of this tutorial is available at the bottom of this page.

There are 4 steps required in creating a custom annotation:

1) Create a custom annotation by deriving from Annotation. This class contains the annotation rendering.
2) Create an factory for the annotation. The factory is used to create, load and save annotations.
3) Register your annotation with the AnnotationStore.
4) Ensure the AnnotationService is enabled. Optionally when the annotation is text-based, you need an enabled TextSelection instance.

The 4 steps are described in detail below.

This tutorial demonstrates a so-called blackout annotation. A blackout annotation can be used to hide text in a document.

1) Create a DocumentViewer project in Visual Studio 2010. Follow the steps in the How To Create My First Document Viewer tutorial to create a simple project if you haven't done so already.

2) Add a new class called Blackout. This is the annotation class that contains the rendering logic. Blackout is an annotation that is associated with text and a abstract base class TextAnnotation is available for the text annotations. Other free-form annotations such as ink and shape annotation are not associated with text and therefore do not need to derive from TextAnnotation.

Modify the contents of the Blackout annotation like so:

public class Blackout : TextAnnotation
{
  public static readonly XName TypeBlackout = XName.Get("Blackout", AnnotationStore.NamespaceAnnotations);

  public Blackout() : base(TypeBlackout) { }

  protected override void RenderGlyphsAnnotation(AnnotationRenderContext context, Glyphs glyphs, Rect rectangle)
  {
    context.AnnotationLayer.Children.Add(new Path() {
        Fill = new SolidColorBrush(Colors.Black),
        Data = new RectangleGeometry() { Rect = rectangle }
    });
  }
}

Annotation types are uniquely identified with an XML name consisting of a name and namespace. The XML based annotation store uses the unique name to identify the different stored annotations.

3) Create a factory for the Blackout annotation by adding a class called BlackoutFactory. Derive from TextAnnotationFactory<T> and implement its abstract methods like so:

public class BlackoutFactory : TextAnnotationFactory<Blackout>
{
  public BlackoutFactory() : base(Blackout.TypeBlackout) { }

  public override Blackout CreateAnnotation()
  {
    return new Blackout();
  }

  protected override void ReadAnnotationProperties(Blackout annotation, XElement source)
  {
    // blackout has no properties
  }

  protected override void WriteAnnotationProperties(Blackout annotation, XElement target)
  {
    // blackout has no properties
  }
}

Since our Blackout annotation does not have any additional properties, the Read and Write annotation properties implementations are empty. You could for instance add a property such as Color to allow different blackout colors. Our annotation implementation always uses the color Black as shown above.

4) The annotation and its factory are now complete. Now we need to create an annotation store, enable the annotation service and enable text selection in our document viewer. Add an annotation store and text selection object in a XAML resource dictionary:

<Grid.Resources>
  <doc:AnnotationStore x:Key="AnnotationStore" />
  <doc:TextSelection x:Key="Selection" IsEnabled="True" />
</Grid.Resources>

5) Make sure the document viewer uses this enabled text selection instance:

<doc:DocumentViewer Selection="{StaticResource Selection}">

6) Register the BlackoutFactory with the annotation store and enable the AnnotationService for the document viewer in code.

// use the annotation store declared in XAML
var annotations = (AnnotationStore)LayoutRoot.Resources["AnnotationStore"];

// register the custom annotation factories
annotations.RegisterFactory(new BlackoutFactory());

// enable annotation service
var service = new AnnotationService(this.Viewer);
service.Enable(annotations);

7) Everything is now in place, we only need to add UI controls to add and remove the blackout annotation. Use of commands is highly recommended, but for brevity we add a button and implement a click event handler in the code behind. The attached source code at the end of this tutorial does contain the proper command implementation and uses a context menu to display the annotation actions.

Add a button to the UI and implement its click event handler. When the button is clicked, we want to create a blackout annotation based on the current selected text.

// access the annotation service
var service = AnnotationService.GetService(this.Selection.TextContainer);
if (service != null && service.IsEnabled) {
  // create and store a blackout for the current selection
  var factory = new BlackoutFactory();
  factory.CreateTextAnnotationForSelection(service);
  
  // and clear the current selection              
  this.Selection.Unselect();
}

Build and run the project, select some text and hit the button. The selected text is now blacked out.

Note: for the tutorial source code to compile you also need to add a reference to System.Windows.Controls.Input.Toolkit.dll. The assembly is available in the Silverlight Toolkit.

Tutorial source code: CustomAnnotation.zip

Last edited Jul 4, 2011 at 7:26 PM by kozw, version 9

Comments

TheWizard Dec 6, 2011 at 11:38 PM 
this.Selection => this.Viewer.Selection