WPF DocumentViewer : Navigate using internal link

2019-06-06 06:17发布

In WPF I have a DocumentViewer inside a Frame control. The DocumentViewer shows an XPS document generated with MS Word. The document contains a Table of Content to help navigate through the document.

The DocumentViewer allows the user to click these links and navigate to the corresponding pages, as long as the DocumentViewer is placed in a control that allows for navigation (e.g. a Frame).

When the user navigates for the first time, the DocumentViewer does not jump accurately to the linked location. The further away in the document, the larger the space between the jumped location and the actual location becomes. It looks like it is off by a certain amount per page. After this first link click, the navigation works perfectly fine.

When navigating back using the navigation buttons on the frame, the link accuracy behaves pore again as soon as the very first view is loaded.

According to this post, a new DocumentViewer is being created after clicking on the link for the first time. It seems that that creates an instance that works as expected.

Why is the initial instance not navigating accurately and how to solve this?

The code snipped below can be used to reproduce the issue.

XAML inside a Window:

<Frame>
    <Frame.Content>
        <DocumentViewer Name="docViewer" />
    </Frame.Content>
</Frame>

Code behind:

    public MainWindow()
    {
        InitializeComponent();
        LoadDoc();
    }

    private void LoadDoc()
    {
        XpsDocument xpsDoc = new XpsDocument("test.xps", FileAccess.Read);
        docViewer.Document = xpsDoc.GetFixedDocumentSequence();
    }

The test.xps document contains a TOC and two chapters with around 40 pages in between them. The issue becomes clear when navigating to the second chapter (it is off by 3 pages).

1条回答
Evening l夕情丶
2楼-- · 2019-06-06 06:55

After almost two years I've revisited this issue and found the solution.

As can be seen in my original post, I used a Frame with its content set to a DocumentViewer. The Frame is used to enable navigation within the XPS document.

Some details:

When the document loads for the first time, the actual Content of the Frame is set to the DocumentViewer. The Source of the Frame is null. When clicking a link in the document, the Frame navigates to the location, but has poor accuracy, as described in my question above. Behind the scenes, the Frame has changed its Content to an instance of FixedDocumentSequence and its Source is set to the clicked Uri. From now on, the navigation works perfectly accurate.

The solution:

The solution is actually very simple. Instead of putting a DocumentViewer inside the Frame and setting the DocumentViewer.Document property to the actual FixedDocumentSequence, the Frame.Source property should be set to the Uri of the FixedDocumentSequence.

FixedDocumentSequence implements the explicit interface property IUriContext.BaseUri that can be used to retrieve the Uri.

In my code, I use a binding to set the source:

<UserControl x:Class="XPSDocumentView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:base="clr-namespace:System.Windows.Markup;assembly=System.Xaml" >

    <Grid>
        <Frame Margin="5" NavigationUIVisibility="Hidden" 
               Source="{Binding Path=Document.(base:IUriContext.BaseUri)}" />
    </Grid>

</UserControl>

In code behind you could accomplish the same by doing this:

XpsDocument xpsDoc = new XpsDocument(Path, FileAccess.Read);
FixedDocumentSequence document = xpsDoc.GetFixedDocumentSequence();
frame.Source = ((System.Windows.Markup.IUriContext)document).BaseUri;
查看更多
登录 后发表回答