Running on Empty

The few things I know, I like to share.

Using FlowDocument XAML to print XPS Documents. (Part 2)

Introduction.

Welcome to Part 2 of the Using FlowDocument XAML to print XPS Documents.  This article will demonstrate using DataContext to add usefulness to Part 1 of this series.

Creating a “useful” XAML FlowDocument.

First, we need to create a FlowDocument that implements element Binding.  For lack of a better word, I will use “element” to describe a C# class that is used to hold data.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Paragraph FontFamily="Arial">
    <StackPanel>
      <TextBlock Text="{Binding Path=Sample}"/>
    </StackPanel>
  </Paragraph>
</FlowDocument>

Okay, again not an extremely useful example, trust me, keep reading it gets better.  DataBinding a XAML FlowDocumentFinally, something useful, we get to actually see some data in our FlowDocument. 

There are several ways to bind data in WPF, some declarative, some programmatic and some that are a cross between the two.  Knowing which DataBinding method to use is the trick.  In this sample, since we don’t have a code behind we are going to use DataContext, which is a cross declarative/programmatic mix.  You have already done the declarative piece in the line.

<TextBlock Text="{Binding Path=Sample}"/>

First, create a class to hold some data, this will be our binding element.

namespace Part2
{
    public class SampleClass
    {
        private string _sample;
        public string Sample
        {
            get { return _sample; }
            set { _sample = value; }
        }
    }
}

Next, create or refactor the RenderFlowDocumentTemplate method from Part 1.

public static IDocumentPaginatorSource RenderFlowDocumentTemplate(string templatePath, object dataContextObject)
{
    string rawXamlText = "";
    using (StreamReader streamReader = File.OpenText(templatePath))
    {
        rawXamlText = streamReader.ReadToEnd();
    }

    FlowDocument document = XamlReader.Load(new XmlTextReader(new StringReader(rawXamlText))) as FlowDocument;
    if (dataContextObject != null)
    {
        document.DataContext = dataContextObject;
    }
    return document;
}

The main addition is the dataContextObject.  I used object base class to make this example more generic, checking for null is simply a precaution, it is not really necessary.

Bringing it together… Again.

Simply create an instance of your element class and pass it to your new and improved RenderFlowDocumentTemplate method.

private void PrintFuelingButton_Click(object sender, RoutedEventArgs e)
{
    SampleClass sample = new SampleClass();
    sample.Sample = "FlowDocument DataBinding Sample";

    IDocumentPaginatorSource flowDocument =
        XamlTemplatePrinter.RenderFlowDocumentTemplate(Path.Combine(Environment.CurrentDirectory, "XamlDocumentTemplate.xaml"), sample);
    PrintDialog flowPrintDialog = XamlTemplatePrinter.GetPrintDialog();
    if (flowPrintDialog == null)
        return;
    PrintQueue flowPrintQueue = flowPrintDialog.PrintQueue;
    XamlTemplatePrinter.PrintFlowDocument(flowPrintQueue, flowDocument.DocumentPaginator);
}

Obviously, this is a great improvement to Part 1, future articles will focus on dynamic XAML uses and FixedDocument.  As always, I look forward to reading comments and suggestions.

January 2, 2008 Posted by | C#, WPF, XAML, XPS | 5 Comments