Annotated FlowDocument Viewer with Comments Pane
A new WPF 3.5 feature in the Annotations area is the capability to match annotations with the corresponding annotated objects. For example, you can now implement a document viewing application that has a comments pane, where the paragraph that contains the corresponding comment (a text sticky note in this case) is brought into view if you make a selection. A simplified version of such an application may look like this:
The list box is bound to the list of annotations. In particular, it uses a converter (IValueConverter) to turn the content of each annotation (64-bit binary data) into text strings. The following is the implementation of the converter:
public class AnnotationDataConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Convert 64 bit binary data into an 8 bit byte array and load
// it into a memory buffer
byte[] data = System.Convert.FromBase64String(value as string);
using (MemoryStream buffer = new MemoryStream(data))
{
// Convert memory buffer to object and return text
Section section = (Section)XamlReader.Load(buffer);
TextRange range = new TextRange(section.ContentStart, section.ContentEnd);
return range.Text;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
The data template that the list box uses does most of the binding work:
<DataTemplate x:Key="annotationDataTemplate">
<TextBlock Margin="5" TextWrapping="Wrap">
<TextBlock FontWeight="Bold" TextWrapping="Wrap">
[<TextBlock Text="{Binding Path=CreationTime}" />]
</TextBlock>
<TextBlock
Text="{Binding Path=Cargos[1].Contents[0].InnerText,Converter={StaticResource annotationDataConverter}}"
TextWrapping="Wrap" />
</TextBlock>
</DataTemplate>
When you make a selection from the comments pane, this event handler finds out where the annotation is anchored to and brings the corresponding paragraph into view:
void annotationsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Annotation comment = (sender as ListBox).SelectedItem as Annotation;
if (comment != null)
{
// IAnchorInfo info;
// service is an AnnotationService object
// comment is an Annotation object
info = AnnotationHelper.GetAnchorInfo(this.service, comment);
TextAnchor resolvedAnchor = info.ResolvedAnchor as TextAnchor;
TextPointer textPointer = (TextPointer)resolvedAnchor.BoundingStart;
textPointer.Paragraph.BringIntoView();
}
}
This application, which is attached and works with Orcas Beta 2 bits, also sorts the comments by creation time and templates the list box so that it is a text-wrapping scrollable area. Check it out!