WPF:Documents文檔--Annomation批註(1)

AnnotatedDocumentViewer批註文檔瀏覽

clipboard.png

一、實現功能:數組

  1. 文檔右鍵菜單建立和刪除批註命令
  2. ListBox顯示批註建立時間(排序)和內容
  3. 選定一個批註,流文檔顯示到對應批註錨定

二、關注詞:架構

  1. 批註XML架構及內容組織:
  2. AnnotationService+AnnotationStore+IAnchorInfo
  3. Convert.FromBase64String+XamlReader.Load+TextRange
  4. AnnotationService.Enable(AnnotatinoStore)

三、靜態組織:
界面xaml:app

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

ListBox模板Template:簡單設置豎滾動條+StackPanel容器ide

<!--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的數據模板:spa

  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格式數據:code

  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;
}

主窗口內容須要一些私有字段協助運行:xml

private IAnchorInfo _info;//提供批註的錨定信息類
private AnnotationService _service;//批註服務類
private AnnotationStore _store;//批註存儲區
private Stream _stream;

四、運行流程:
主窗口初始化時設置好批註存儲導入及綁定到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();
    }
}
相關文章
相關標籤/搜索