AnnotatedDocumentViewer批注文档浏览
1、实现功能:
- 文档右键菜单创建和删除批注命令
- ListBox显示批注创建时间(排序)和内容
- 选定一个批注,流文档显示到对应批注锚定
2、关注词:
- 批注XML架构及内容组织:
- AnnotationService+AnnotationStore+IAnchorInfo
- Convert.FromBase64String+XamlReader.Load+TextRange
- 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的数据模板:
- TextBlock内容中[]里面绑定批注数据区AnnotationStore的时间属性CreationTime,
- 其后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格式数据:
- 把Base64格式的string 使用Convert.FromBase64String转换为字节数字byte[]
- MemoryStream初始化字节数组为内存流(使用using自释放内存)
- 使用XamlReader.Load把(Xaml输入的数据)内存流转换为Section根元素。
- 把Section元素的内容构造为TextRange元素。
- 返回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显示
- AnnotationService.Enable(AnnotationStory)
- 绑定批注存储的内容到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());
}
批注绑定方法代码:
- ListBox数据上下文赋值为批注集
- 使用批注的创建时间属性CreationTime作为降序,构造排序描述类SortDescription
- 获取ListBox的集合视图ICollectionSource
- 集合视图的排序集合添加上述的排序描述实例
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);
}
批注选择时定位显示批注锚定位置:
- 选择项转换为批注类Annotation,并在AnnotationHelper下获取批注信息
- 获取批注实例的锚定对象TextAnchor
- 获取锚定对象的文本定位位置点TextPointer
- 把锚定点当前位置段落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();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。