DEV Community

Cover image for Navigate PDF Annotations in a TreeView Using WPF PDF Viewer
Jollen Moyani for Syncfusion, Inc.

Posted on • Originally published at syncfusion.com on

Navigate PDF Annotations in a TreeView Using WPF PDF Viewer

TL;DR: Annotations in PDFs are like digital sticky notes, helpful for comments and information. This blog discusses organizing them effectively using a tree view grouped by pages, making navigation smooth with Syncfusion WPF PDF Viewer.

Annotations are invaluable tools for adding comments, notes, and other relevant information to PDF documents. When managing these annotations, organizing them in a structured manner can significantly enhance navigation and usability.

In this blog, we’ll explore how to efficiently showcase PDF annotations in a tree view, grouped by the pages they belong to, and seamlessly navigate through them using the Syncfusion WPF PDF Viewer control.

Let’s get started!

Create a WPF application and add dependencies

First, create a new WPF application and install the following NuGet packages in it:

Organize PDF Viewer and TreeView

Let’s create a main content page in the MainWindow.xaml file and add the Syncfusion WPF PDF Viewer and WPF Framework’s TreeView as the children to it. Then, add the event handler for the TreeView’s SelectedItemChanged event.

Refer to the following code example.

<Window
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       xmlns:local="clr-namespace:WPF_PDFViewer_Annotations"
       xmlns:PdfViewer="clr-namespace:Syncfusion.Windows.PdfViewer;assembly=Syncfusion.PdfViewer.WPF" xmlns:syncfusion="http://schemas.syncfusion.com/wpf" x:Class="WPF_PDFViewer_Annotations.MainWindow"
       mc:Ignorable="d"
       Title="MainWindow" Height="450" Width="800" WindowState="Maximized">
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition Width="*"/>
   <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <PdfViewer:PdfViewerControl x:Name="pdfViewer"/>
  <TreeView x:Name="treeView" Grid.Column="1" SelectedItemChanged="treeView_SelectedItemChanged"/>
 </Grid>
</Window>
Enter fullscreen mode Exit fullscreen mode

In the MainWindow.xaml.cs file, let’s set the theme for Syncfusion WPF PDF Viewer control and trigger the DocumentLoaded event with the AnnotationChanged event for all supported annotations.

Refer to the following code example.

public MainWindow()
{
    SfSkinManager.SetTheme(this, new Theme("FluentLight"));
    InitializeComponent();
    pdfViewer.DocumentLoaded += PdfViewer_DocumentLoaded;
    pdfViewer.FreeTextAnnotationChanged += PdfViewer_FreeTextAnnotationChanged;
    pdfViewer.StickyNoteAnnotationChanged += PdfViewer_StickyNoteAnnotationChanged;
    pdfViewer.InkAnnotationChanged += PdfViewer_InkAnnotationChanged;
    pdfViewer.TextMarkupAnnotationChanged += PdfViewer_TextMarkupAnnotationChanged;
    pdfViewer.ShapeAnnotationChanged += PdfViewer_ShapeAnnotationChanged;
    pdfViewer.StampAnnotationChanged += PdfViewer_StampAnnotationChanged;
    pdfViewer.Load("../../Data/Sample Document.pdf");
    treeView.Background = new SolidColorBrush(Color.FromRgb(235,235,238));
}
Enter fullscreen mode Exit fullscreen mode

After loading the document information in the DocumentLoaded event, add the supported annotation details with the respective page in the TreeView.

Refer to the following code example.

private void PdfViewer_DocumentLoaded(object sender, EventArgs args)
{
    PdfLoadedDocument loadedDocument = pdfViewer.LoadedDocument;
    treeView.Items.Clear();
    for (int i = 0; i < loadedDocument.Pages.Count; i++)
    {
        TreeViewItem viewItem = new TreeViewItem();
        if (loadedDocument.Pages[i].Annotations.Count > 0)
        {
            viewItem.Header = "PAGE - " + (i + 1);
            viewItem.FontSize = 16;
            treeView.Items.Add(viewItem);
            viewItem.IsExpanded = true;
            for (int j = 0; j < loadedDocument.Pages[i].Annotations.Count; j++)
            {
                PdfLoadedAnnotation annotation = loadedDocument.Pages[i].Annotations[j] as PdfLoadedAnnotation;
                if (annotation is PdfLoadedInkAnnotation || annotation is PdfLoadedTextMarkupAnnotation || annotation is PdfLoadedEllipseAnnotation
                    || annotation is PdfLoadedLineAnnotation || annotation is PdfLoadedRectangleAnnotation || annotation is PdfLoadedSquareAnnotation
                    || annotation is PdfLoadedCircleAnnotation || annotation is PdfLoadedFreeTextAnnotation || annotation is PdfLoadedRubberStampAnnotation
                    || annotation is PdfLoadedPopupAnnotation || annotation is PdfLoadedPolygonAnnotation || annotation is PdfLoadedPolyLineAnnotation)
                {
                    string name = annotation.ToString();
                    string[] annotNames = name.Split('.');
                    TreeViewItem childItem = new TreeViewItem();
                    childItem.Header = annotation.Type.ToString();
                    childItem.Tag = annotation;
                    viewItem.Items.Add(childItem);
                }
            }
            if (viewItem.Items.Count == 0)
                treeView.Items.Remove(viewItem);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

While adding/removing the annotations, we need to update the respective annotation data in the treeview. To do so, we will use the AnnotationChanged event to update the treeview values.

Refer to the following code example.

private void PdfViewer_InkAnnotationChanged(object sender, InkAnnotationChangedEventArgs e)
{
    PerformAddorRemoveAnnotations("InkAnnotation",e.Name,e.Action,e.PageNumber);
}

private void PerformAddorRemoveAnnotations(string name ,string annotationName ,AnnotationChangedAction action ,int annotationPageNumber)
{
    if (action == AnnotationChangedAction.Add)
    {
        PdfLoadedDocument lDoc = pdfViewer.LoadedDocument;
        if (treeView.Items.Count > 0)
        {
            for (int i = 0; i < treeView.Items.Count; i++)
            {
                string[] page = (treeView.Items[i] as TreeViewItem).Header.ToString().Split(' ');
                int pageNumber = int.Parse(page[page.Length - 1]);
                if (pageNumber == annotationPageNumber)
                {
                    TreeViewItem item = treeView.Items[i] as TreeViewItem;
                    TreeViewItem childItem = new TreeViewItem();
                    for (int j = 0; j < lDoc.Pages[annotationPageNumber - 1].Annotations.Count; j++)
                    {
                        if (lDoc.Pages[annotationPageNumber - 1].Annotations[j].Name == annotationName)
                        {
                            childItem.Header = name;
                            childItem.Tag = lDoc.Pages[annotationPageNumber - 1].Annotations[j];
                            break;
                        }
                    }
                    item.Items.Add(childItem);
                    break;
                }
                else if (pageNumber > annotationPageNumber)
                {
                    TreeViewItem item = new TreeViewItem();
                    item.Header = "PAGE - " + annotationPageNumber;
                    treeView.Items.Insert(i, item);
                    TreeViewItem childItem = new TreeViewItem();
                    for (int j = 0; j < lDoc.Pages[annotationPageNumber - 1].Annotations.Count; j++)
                    {
                        if (lDoc.Pages[annotationPageNumber - 1].Annotations[j].Name == annotationName)
                        {
                            childItem.Header = name;
                            childItem.Tag = lDoc.Pages[annotationPageNumber - 1].Annotations[j];
                            break;
                        }
                    }
                    item.Items.Add(childItem);
                }
            }
        }
        else
        {
            TreeViewItem item = new TreeViewItem();
            item.Header = "PAGE - " + annotationPageNumber;
            item.FontSize = 16;
            treeView.Items.Add(item);
            TreeViewItem childItem = new TreeViewItem();
            for (int j = 0; j < lDoc.Pages[annotationPageNumber - 1].Annotations.Count; j++)
            {
                if (lDoc.Pages[annotationPageNumber - 1].Annotations[j].Name == annotationName)
                {
                    childItem.Header = name;
                    childItem.Tag = lDoc.Pages[annotationPageNumber - 1].Annotations[j];
                    break;
                }
            }
            item.Items.Add(childItem);
        }
    }
    else if (action == AnnotationChangedAction.Remove)
    {
        for (int i = 0; i < treeView.Items.Count; i++)
        {
            string[] page = (treeView.Items[i] as TreeViewItem).Header.ToString().Split(' ');
            int pageNumber = int.Parse(page[page.Length - 1]);
            if (pageNumber == annotationPageNumber)
            {
                TreeViewItem item = treeView.Items[i] as TreeViewItem;
                for (int j = 0; j < item.Items.Count; j++)
                {
                    PdfLoadedAnnotation loadedAnnotation = (item.Items[j] as TreeViewItem).Tag as PdfLoadedAnnotation;
                    PdfAnnotation annotation = (item.Items[j] as TreeViewItem).Tag as PdfAnnotation;
                    if (loadedAnnotation != null)
                    {
                        if (annotationName == loadedAnnotation.Name)
                        {
                            item.Items.RemoveAt(j);
                            break;
                        }
                    }
                    else if (annotation != null)
                    {
                        if (annotationName == annotation.Name)
                        {
                            item.Items.RemoveAt(j);
                            break;
                        }
                    }
                }
                if (item.Items.Count == 0)
                {
                    treeView.Items.RemoveAt(i);
                }
                break;
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Implement navigation on annotation selection

Next, we will implement the code to select an annotation in the treeview that will navigate you to its corresponding annotation position in the PDF. PDF Viewer provides support to select and bring an annotation to view programmatically using the overload SelectAnnotation method with BringIntoView Parameter.

The annotation’s name and true value for BringIntoView should be passed as a parameter to select and bring an annotation into view, which should be handled in the SelectedItemChanged event of TreeView.

Implement the following code in the treeView_SelectedItemChanged method, which is present in the MainWindow.xaml.cs file.

private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    var selectedItem = treeView.SelectedItem as TreeViewItem;
    if (selectedItem != null && !selectedItem.Header.ToString().Contains("PAGE"))
    {
        PdfLoadedAnnotation loadedAnnotation = selectedItem.Tag as PdfLoadedAnnotation;
        PdfAnnotation annotation = selectedItem.Tag as PdfAnnotation;
        if (loadedAnnotation != null)
            pdfViewer.SelectAnnotation(loadedAnnotation.Name, true);
        else if (annotation != null)
            pdfViewer.SelectAnnotation(annotation.Name, true);
    }
}
Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Viewing annotations in a tree view and navigating to them using Syncfusion WPF PDF Viewer

Viewing annotations in a tree view and navigating to them using Syncfusion WPF PDF Viewer

GitHub reference

Also, refer to view PDF annotations in the tree view and navigate to them using the Syncfusion WPF PDF Viewer demo on GitHub.

Conclusion

Thanks for reading! In this blog, we have seen how to view annotations in a PDF document using a tree view and easily navigate to them using Syncfusion WPF PDF Viewer control. Try out the steps given in this blog and leave your feedback in the comments section given below.

The existing customers can download the latest version of Essential Studio from the License and Downloads page. If you are new, try our 30-day free trial to explore our incredible features.

If you require assistance, please don’t hesitate to contact us via our support forums, support portal, or feedback portal. We are always eager to help you!

Related blogs

Top comments (0)