Posted by: Cirilo Meggiolaro | 01/23/2009

Tip of the day #101 – Persisting annotations with XmlStreamStore class (WPF)

Annotation is a nice feature from Windows Presentation Foundation (WPF) that provides you a way to add comments to a text as the same way we can add comments on a Excel spreadsheet cell.

The following picture shows a WPF application with a text highlighted and an annotation added to that text:

Picture 1 - A simple WPF application with an annotation.

Picture 1 - A simple WPF application with an annotation.

The application showed above allow us to add and remove annotations (also known as Stick Note) and persist them to a xml file using a XmlStreamStore class in conjunction to a FileStream object to save the annotations to a Xml file.

XmlStreamStore class

The XmlStreamStore class inherits from the abstract AnnotationStore class. The following constructors are available for that class:

public XmlStreamStore(Stream stream);
public XmlStreamStore(Stream stream, IDictionary<Uri, IList<Uri>> knownNamespaces);

How to…

We are going to create the application from the scratch. As the main topic of this tip is to show how to persist the annotations I’ll assume that you have some experience on XAML layout. Anyway the steps are described below:

  1. Add a FlowDocumentReader and set its name to docReader;
  2. Add a FlowDocument inside the FlowDocumentReader;
  3. Create two paragraph controls and add some text to each one;
  4. Add a namespace to the System.Windows.Annotations available under the PresentationFramework assembly;
  5. Add a button to add the annotation. Set its command to AnnotationService.CreateTextStickyNoteCommand and set the CommandTarget to {Binding ElementName=docReader};
  6. Add a button to delete the annotation. Set its command to AnnotationService.DeleteStickyNotesCommand and set the CommandTarget to {Binding ElementName=docReader};
  7. Add the Initialized and Closed event handlers for the window.

The XAML must be similar to the following:

<Window x:Class=”Tip101.Window1″
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;
xmlns:annot=”clr-namespace:System.Windows.Annotations;assembly=PresentationFramework”
Title=”Annotation Tip” Height=”388″ Width=”521″ Initialized=”Window_Initialized” Closed=”Window_Closed”>
 <StackPanel>
  <FlowDocumentReader x:Name=”docReader” Height=”300″>
   <FlowDocument>
    <Paragraph>
      Add some text to here.
    </Paragraph>
    <Paragraph>
      Add more text to here.
    </Paragraph>
   </FlowDocument>
  </FlowDocumentReader>
  <StackPanel>
   <Button Content=”Add Sticky Note” Command=”annot:AnnotationService.CreateTextStickyNoteCommand” CommandTarget=”{Binding ElementName=docReader}” Width=”150″></Button>
   <Button Content=”Delete Sticky Note” Command=”annot:AnnotationService.DeleteStickyNotesCommand” CommandTarget=”{Binding ElementName=docReader}” Width=”150″></Button>
  </StackPanel>
 </StackPanel>
</Window>

On the code behind:

  • Create a FileStream object on the form scope to hold the main output;

FileStream stream;

  • On the Initialized event instantiate the FileStream object declared on the previous step;

stream = new FileStream(“tip102annotations.xml”, FileMode.OpenOrCreate);

  • Create an AnnotationStore instance from the XmlStreamStore class;

AnnotationStore store = new XmlStreamStore(stream);

  • Set the store AutoFlush property to true to make sure to flush data every time an annotation is changed;

store.AutoFlush = true;

  • Enable the AnnotationService object passing the store as parameter;

service.Enable(store);

The following code shows the final Initialized event code:

/// Creates an annotation service based on
/// a FlowDocumentReader control.

AnnotationService service = AnnotationService.GetService(docReader);

/// Checks if the service has not been instantiated.
if (service == null)
{
    /// Instantiates a FileStream object.
    stream = new FileStream(“tip100annotations.xml”, FileMode.OpenOrCreate);

    /// Instantiates the annotation service.
    service = new AnnotationService(docReader);

    /// Creates an annotation store using the derived
    /// XmlStreamStore object and the FileStream object.
    AnnotationStore store = new XmlStreamStore(stream);

    /// Sets the AutoFlush property to true to ensure
    /// that every annotation is flushed to the store automatically.
    store.AutoFlush = true;

    /// Enables the annotation service.
    service.Enable(store);
}

To finalize we need to close the main stream and disable the annotation service on the Window_Closed event:

    stream.Close();

    The final code for the Window_Closed event must be similar to the following:

    /// Tries to get the annotation service.
    AnnotationService service = AnnotationService.GetService(docReader);

    /// If the annotation service is valid and is enabled
    if (service != null && service.IsEnabled)
    {
        /// Disables the service.
        service.Disable();

        /// Closes the stream
        stream.Close();
    }

    Advertisements

    Responses

    1. HI, first of all thanks for sharing the great knowledge, i am havin gjust 1 problem which is —

      How to save the notification and reload them on the next time user opens the reder?

      Please reply, as i need this urent thanks in advance…


    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s

    Categories

    %d bloggers like this: