Enabling the DocumentViewer WYSIWYG mode

Let's dive a little bit deeper in WPF's DocumentViewer Control. As I explained in my previous post, you can generate any report at run time as a .xps file by writing XAML yourself. In the real world you only want to do this for extremely simple reports, because creating, debugging, and extending reports like this, is undoubtedly a pain. That's why I suggest to build your reports as a UserControl. This has two advantages: at design time you can leverage Visuals Studio's Designers and Compilers, while at run time you have access to all WPF's binding features, and your own resources, templates and type converters. Here's a small example of a demo user control (no binding, type conversion, etc.):

<UserControl x:Class="DockOfTheBay.ReportUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="30cm" Width="20cm">
    <StackPanel>
        <TextBlock Margin="10">Dock of the Bay</TextBlock>
        <Ellipse Margin="10" 
                 Height="50" 
                 Width="50" 
                 Fill="Chartreuse" 
                 Stroke="MidnightBlue" 
                 HorizontalAlignment="Left" />
    </StackPanel>
</UserControl>


While you are developing the Report, Visual Studio will reveal the looks of it and check syntax for you, providing you with something very close to a WYSIWYG experience. For rendering the UserControl in a DocumentViewer, you can build a broadly usable form that takes a UIElement, e.g. as a property or as a parameter in the constructor. Here's how such a form can look like:

XAML:

<Window x:Class="DockOfTheBay.XpsWysiwyg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="XPS Wysiwyg Sample" Height="300" Width="300">
        <DocumentViewer Name="documentViewer1" />
</Window>


C#:

namespace DockOfTheBay
{
    using System;
    using System.IO;
    using System.Windows;
    using System.Windows.Xps.Packaging;
    using System.Windows.Xps.Serialization;
 
    public partial class XpsWysiwyg : Window
    {
        public XpsWysiwyg()
        {
            InitializeComponent();
        }
 
        public UIElement ElementToRender
        {
            set
            {
                // Create Temporary file
                string tempFileName = System.IO.Path.GetTempPath();
                tempFileName = System.IO.Path.Combine(tempFileName, Guid.NewGuid().ToString() + ".xps");
 
                // Save UserControl as XPS
                XpsDocument document = new XpsDocument(tempFileName, FileAccess.ReadWrite);
 
                // Packaging Policy is the destination package
                XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document);
 
                // Serialization Manager controls how the XPS looks like
                // by the way: there's also an Async version
                XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false);
                serializationMgr.SaveAsXaml(value);
 
                // Display
                documentViewer1.Document = document.GetFixedDocumentSequence();
 
                // Cleanup
                document.Close();
                File.Delete(tempFileName);
            }
        }
    }
}


All you need to do to visualize the report in your application, is opening the Report Form and hook it to the UserControl you want to display:

XpsWysiwyg reportForm = new XpsWysiwyg();
reportForm.ElementToRender = new ReportUserControl();
reportForm.Show();


This is the result:



This is how the Form looks in a real application, displaying an official Belgian Cancer Registration Form (with dummy data):

1 comment:

  1. Thank you very much, this step has got me a lot closer to having a data bound xps document in the documentviewer. Data binding doesn't seem to work though, and ideas?

    ReplyDelete