1

AnnotatedDocumentViewer批注文档浏览

clipboard.png

1、实现功能:

  1. 文档右键菜单创建和删除批注命令
  2. ListBox显示批注创建时间(排序)和内容
  3. 选定一个批注,流文档显示到对应批注锚定

2、关注词:

  1. 批注XML架构及内容组织:
  2. AnnotationService+AnnotationStore+IAnchorInfo
  3. Convert.FromBase64String+XamlReader.Load+TextRange
  4. AnnotationService.Enable(AnnotatinoStore)

3、静态组织:
界面xaml:

<!-- Annotations List -->
<ListBox
  Name="annotationsListBox"
  Grid.Row="2"
  SelectionChanged="annotationsListBox_SelectionChanged"
  ItemsSource="{Binding}"
  Template="{StaticResource AnnotationsListTemplate}"
  ItemTemplate="{StaticResource AnnotationDataTemplate}" />

ListBox模板Template:简单设置竖滚动条+StackPanel容器

<!--To Replace ListBox Template with Template that Allows TextWrapping and
  also provides a vertical scrollbar when the wrapped text extends below 
  the bottom of the list box -->
<ControlTemplate x:Key="AnnotationsListTemplate">
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" >
        <StackPanel IsItemsHost="True" />
    </ScrollViewer>
</ControlTemplate>

ListItem的数据模板:

  1. TextBlock内容中[]里面绑定批注数据区AnnotationStore的时间属性CreationTime,
  2. 其后TextBlock.Text绑定到批注数据区AnnotationStore的(AnnotationResource)货物Cargos[1]的内容Content[0]的(string)InnerText
<!-- Data Template for Annotation Item that shows when an annotation was
  created, and what data the annotation contains.  -->
<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="NoWrap" />
  </TextBlock>
</DataTemplate>

绑定转换string》TextRange.Text(Xml包含的Xaml数据转换为文本Text格式数据:

  1. 把Base64格式的string 使用Convert.FromBase64String转换为字节数字byte[]
  2. MemoryStream初始化字节数组为内存流(使用using自释放内存)
  3. 使用XamlReader.Load把(Xaml输入的数据)内存流转换为Section根元素。
  4. 把Section元素的内容构造为TextRange元素。
  5. 返回TextRang包含的文本Text.
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
        var data = System.Convert.FromBase64String(value as string);
        using (var buffer = new MemoryStream(data))
        {
            // Convert memory buffer to object and return text
            var section = (Section) XamlReader.Load(buffer);
            var range = new TextRange(section.ContentStart, section.ContentEnd);
            return range.Text;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null;
}

主窗口内容需要一些私有字段协助运行:

private IAnchorInfo _info;//提供批注的锚定信息类
private AnnotationService _service;//批注服务类
private AnnotationStore _store;//批注存储区
private Stream _stream;

4、运行流程:
主窗口初始化时设置好批注存储导入及绑定到ListBox显示

  1. AnnotationService.Enable(AnnotationStory)
  2. 绑定批注存储的内容到ListBox
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    // Load annotations store
    _stream = new FileStream("storage.xml", FileMode.OpenOrCreate);
    _service = new AnnotationService(flowDocumentReader);
    _store = new XmlStreamStore(_stream) {AutoFlush = true};
    _service.Enable(_store);

    // Detect when annotations are added or deleted
    _service.Store.StoreContentChanged +=
        AnnotationStore_StoreContentChanged;

    // Bind to annotations in store
    BindToAnnotations(_store.GetAnnotations());
}

批注内容变动,触发事件,需重新绑定批注内容到ListBox更新显示

private void AnnotationStore_StoreContentChanged(object sender, StoreContentChangedEventArgs e)
{
    // Bind to refreshed annotations store
    BindToAnnotations(_store.GetAnnotations());
}

批注绑定方法代码:

  1. ListBox数据上下文赋值为批注集
  2. 使用批注的创建时间属性CreationTime作为降序,构造排序描述类SortDescription
  3. 获取ListBox的集合视图ICollectionSource
  4. 集合视图的排序集合添加上述的排序描述实例
private void BindToAnnotations(IList<Annotation> annotations)
{
    // Bind to annotations in store
    annotationsListBox.DataContext = annotations;

    // Sort annotations by creation time
    var sortDescription = new SortDescription
    {
        PropertyName = "CreationTime",
        Direction = ListSortDirection.Descending
    };
    var view = CollectionViewSource.GetDefaultView(annotationsListBox.DataContext);
    view.SortDescriptions.Clear();
    view.SortDescriptions.Add(sortDescription);
    
}

批注选择时定位显示批注锚定位置:

  1. 选择项转换为批注类Annotation,并在AnnotationHelper下获取批注信息
  2. 获取批注实例的锚定对象TextAnchor
  3. 获取锚定对象的文本定位位置点TextPointer
  4. 把锚定点当前位置段落Paragraph.BringIntoView滚动到显示出
private void annotationsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var 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(_service, comment);
        var resolvedAnchor = _info.ResolvedAnchor as TextAnchor;
        var textPointer = (TextPointer) resolvedAnchor.BoundingStart;
        textPointer.Paragraph.BringIntoView();
    }
}

最后主窗口关闭时处理批注服务类的关闭:

private void MainWindow_Closed(object sender, EventArgs e)
{
    if (_service != null && _service.IsEnabled)
    {
        _service.Disable();
        _stream.Close();
    }
}

李志玮
22 声望34 粉丝

求索~~


引用和评论

0 条评论