In this post I demonstrate how the methods of a DocumentViewer class may be invoked via MVVM / WPF in order to modify the way an embedded PowerPoint presentation is displayed.
Step 1: Create a new WPF application
Step 2: Create the Main Window view
Just a simple view to house the DocumentViewer control and a button with which to tell it to launch the PowerPoint presentation.
We will fill in the various command binding a bit later.
<Window x:Class="DocumentView.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:local="clr-namespace:DocumentView"
mc:Ignorable="d"
WindowState="{Binding WindowState, Mode=TwoWay}"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<DocumentViewer
Grid.Row="0"
Document="{Binding FixedFixedDocumentSequence}"
Name="DocumentViewPowerPoint"
VerticalAlignment="Top"
HorizontalAlignment="Left" />
<Button
Grid.Row="1"
Command="{Binding Command}"
Width="70" Height="30" Content="Press" />
</Grid>
</Window>
Step 3: Add the event handling classes
EventArgs.cs
using System;
namespace DocumentView
{
public class EventArgs<T> : EventArgs
{
public EventArgs(T value)
{
Value = value;
}
public T Value { get; private set; }
}
}
EventRaiser.cs
using System;
namespace DocumentView
{
public static class EventRaiser
{
public static void Raise(this EventHandler handler, object sender)
{
if (handler != null)
{
handler(sender, EventArgs.Empty);
}
}
public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, T value)
{
if (handler != null)
{
handler(sender, new EventArgs<T>(value));
}
}
public static void Raise<T>(this EventHandler<T> handler, object sender, T value) where T : EventArgs
{
if (handler != null)
{
handler(sender, value);
}
}
public static void Raise<T>(this EventHandler<EventArgs<T>> handler, object sender, EventArgs<T> value)
{
if (handler != null)
{
handler(sender, value);
}
}
}
}
RelayCommand.cs
using System;
using System.Windows.Input;
namespace DocumentView
{
public class RelayCommand<T> : ICommand
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public RelayCommand(Action<T> execute)
: this(execute, null)
{
_execute = execute;
}
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
_execute = execute;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
// Ensures WPF commanding infrastructure asks all RelayCommand objects whether their
// associated views should be enabled whenever a command is invoked
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
CanExecuteChangedInternal -= value;
}
}
private event EventHandler CanExecuteChangedInternal;
public void RaiseCanExecuteChanged()
{
CanExecuteChangedInternal.Raise(this);
}
}
}
Step 4: Create the ViewModel classes
MainWindowViewModel.cs
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Application = Microsoft.Office.Interop.PowerPoint.Application;
using System.Windows.Documents;
using System.Windows.Input;
namespace DocumentView
{
public class MainWindowViewModel : ViewModelBase
{
private ICommand _command;
private DocumentViewer _documentViewer;
public DocumentViewer DocumentViewer
{
get
{
return _documentViewer;
}
set
{
_documentViewer = value;
OnPropertyChanged("DocumentViewer");
}
}
private IDocumentPaginatorSource _fixedDocumentSequence;
public IDocumentPaginatorSource FixedFixedDocumentSequence
{
get
{
return _fixedDocumentSequence;
}
set
{
_fixedDocumentSequence = value;
OnPropertyChanged("DocViewer");
}
}
public ICommand Command
{
get
{
return _command ?? (_command = new RelayCommand(
x =>
{
DocumentViewer = MainWindow.GetInstance();
const string powerPointFile = @"c:\temp\ppt.pptx";
var xpsFile = Path.GetTempPath() + Guid.NewGuid() + ".xps";
var xpsDocument = ConvertPowerPointToXps(powerPointFile, xpsFile);
FixedFixedDocumentSequence = xpsDocument.GetFixedDocumentSequence();
DocumentViewer.Document = FixedFixedDocumentSequence;
DocumentViewer.GoToPage(1);
DocumentViewer.FitToMaxPagesAcross(1);
WindowState = WindowState.Maximized;
DocumentViewer.FitToMaxPagesAcross(1);
}));
}
}
private WindowState _windowState;
public WindowState WindowState
{
get
{
return _windowState;
}
set
{
_windowState = value;
base.OnPropertyChanged("WindowState");
}
}
public MainWindowViewModel()
{
FixedFixedDocumentSequence = null;
WindowState = WindowState.Maximized;
}
private static XpsDocument ConvertPowerPointToXps(string pptFilename, string xpsFilename)
{
var pptApp = new Application();
var presentation = pptApp.Presentations.Open(pptFilename, MsoTriState.msoTrue, MsoTriState.msoFalse,
MsoTriState.msoFalse);
try
{
presentation.ExportAsFixedFormat(xpsFilename, PpFixedFormatType.ppFixedFormatTypeXPS);
}
catch (Exception ex)
{
MessageBox.Show("Failed to export to XPS format: " + ex);
}
finally
{
presentation.Close();
pptApp.Quit();
}
return new XpsDocument(xpsFilename, FileAccess.Read);
}
}
}
ViewModelBase.cs
using System.ComponentModel;
namespace DocumentView
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Step 5: Add the references
Office
Microsoft.Office.Interop.PowerPoint
Reach Framework
Step 6: Update MainWindow.xaml.cs
using System.Windows.Controls;
namespace DocumentView
{
public partial class MainWindow
{
private static DocumentViewer _docViewer;
public MainWindow()
{
InitializeComponent();
_docViewer = DocumentViewPowerPoint;
}
public static DocumentViewer GetInstance()
{
return _docViewer;
}
}
}
Step 7: Run the code
On building and running the app we see the empty DocumentViewer control as shown:
Pressing the button converts the PowerPoint into xps file format and the view property is applied so that the whole slide id displayed completely:
Download sample Visual Studio 2015 project from here:
“Value does not fall within the expected range ” error is coming in “ExportAsFixedFormat” method
Hello,
The word document able to convert and view in XPS format but hyperlinks in the word file are inactive(cursor hovered no response ,not clickable and don’t take me to where they are linked to). How do I fix this?
Thanks!