I have a C# Wpf project in which I have successfully loaded an Xps. file into a Document Viewer. I want to be able to have a variable in my C# code that notices a page change when you scroll the document. So far I have figured out, that there is a function for the xaml code, that automatically changes the page number if you scroll to the next page:
<DocumentViewer x:Name="viewDocument" HorizontalAlignment="Left" VerticalAlignment="Top" Height="Auto" Grid.Row="0" Grid.Column="0" >
<FixedDocument></FixedDocument>
</DocumentViewer>
<TextBlock Text="{Binding ElementName=viewDocument,Path=MasterPageNumber}" Grid.Row="1"/>
My final goal is to sto pthe time the user spends on each page which is why I need to be able to connect the current page number with a variable in my code which I cannot do with the above example. I have tried to implement an INotifyPropertyChanged, but I am fairly new to C# and I cannot find the error. it sets the variable to the first page, but after that it doesn't update.
This is my View Model:
using System; using System.ComponentModel;
namespace Tfidf_PdfOnly {
public class MainViewModel : INotifyPropertyChanged
{
private int _myLabel;
public int MyLabel
{
get
{
return this._myLabel;
}
set
{
this._myLabel = value;
NotifyPropertyChanged("MyLabel");
}
}
public MainViewModel()
{
_myLabel = 55;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
and this is in my Document_Viewer.xaml.cs file
XpsDocument document1 = new XpsDocument(path, System.IO.FileAccess.Read);
//load the file into the viewer
viewDocument.Document = document1.GetFixedDocumentSequence();
MainViewModel vm = new MainViewModel();
this.DataContext = vm;
vm.MyLabel = viewDocument.MasterPageNumber;
To see if it works I bound it to a label on the UI:
<DocumentViewer x:Name="viewDocument" HorizontalAlignment="Left" VerticalAlignment="Top" Height="Auto" Grid.Row="0" Grid.Column="0" >
<FixedDocument></FixedDocument>
</DocumentViewer>
<TextBlock Text="{Binding MyLabel, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Grid.Row="1" HorizontalAlignment="Right"/>
I hope my question is clear, and any help is appreceated!
The DocumentViewer has a property named MasterPageNumber (which is supposed to be the page index of the document). The following sample uses Prism and the Blend SDK (behaviors). The converter is quick-and-dirty. For the timing, you could use StopWatch instances to track how long in between page changes.
MVVM Approach
ViewModel
public class ShellViewModel : BindableBase
{
private int _currentPage;
public string Title => "Sample";
public string DocumentPath => @"c:\temp\temp.xps";
public int CurrentPage
{
get => _currentPage;
set => SetProperty(ref _currentPage, value);
}
public ICommand PageChangedCommand => new DelegateCommand<int?>(i => CurrentPage = i.GetValueOrDefault());
}
View
<Window x:Class="Poc.MainWindow"
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:viewModels="clr-namespace:Poc.ViewModels"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviors="clr-namespace:Poc.Views.Interactivity.Behaviors"
xmlns:converters="clr-namespace:Poc.Views.Converters"
xmlns:controls1="clr-namespace:Poc.Views.Controls"
mc:Ignorable="d"
Title="{Binding Title}" Height="350" Width="525">
<Window.Resources>
<converters:PathToDocumentConverter x:Key="PathToDocumentConverter"></converters:PathToDocumentConverter>
</Window.Resources>
<Window.DataContext>
<viewModels:ShellViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<DocumentViewer Document="{Binding DocumentPath,Converter={StaticResource PathToDocumentConverter}}">
<i:Interaction.Behaviors>
<behaviors:DocumentViewerBehavior PageViewChangedCommand="{Binding PageChangedCommand}"></behaviors:DocumentViewerBehavior>
</i:Interaction.Behaviors>
</DocumentViewer>
<TextBlock Grid.Row="1" Text="{Binding CurrentPage}"></TextBlock>
</Grid>
Behavior
public class DocumentViewerBehavior : Behavior<DocumentViewer>
{
public static readonly DependencyProperty PageViewChangedCommandProperty = DependencyProperty.Register(nameof(PageViewChangedCommand), typeof(ICommand), typeof(DocumentViewerBehavior));
public ICommand PageViewChangedCommand
{
get => (ICommand)GetValue(PageViewChangedCommandProperty);
set => SetValue(PageViewChangedCommandProperty, value);
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PageViewsChanged += OnPageViewsChanged;
}
private void OnPageViewsChanged(object sender, EventArgs e) => PageViewChangedCommand?.Execute(AssociatedObject.MasterPageNumber);
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PageViewsChanged -= OnPageViewsChanged;
}
}
Converter
public class PathToDocumentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var fileInfo = new FileInfo((string)value);
if (fileInfo.Exists)
{
if (String.Compare(fileInfo.Extension, ".XPS", StringComparison.OrdinalIgnoreCase) == 0)
{
return new XpsDocument(fileInfo.FullName, FileAccess.Read).GetFixedDocumentSequence();
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}