C# – Printing a flowdocument with dynamic data in WPF

cflowdocumentnetwpf

I am trying to find a good way to print a flow document in WPF. What I want is to have a possibility to see how the document turns out as I design it, so therefore creating a pure FlowDocument as a XAML is out of the questions (as Visual Studio wont show the design view for it).

So what I have done now is to create a window that contains a FlowDocument like this (some excessive parts have been removed to make the code more consise):

<Window x:Class="MyNamespace.ProjectPrintout...>
  <Grid>
    <FlowDocumentReader>
      <FlowDocument ColumnWidth="500" Name="Document">
        <!-- Header -->
        <Paragraph Name="HeaderText">
          The header will go here
        </Paragraph>
      </FlowDocument>
    </FlowDocumentReader>
  </Grid>
</Window>

This is a bit strange since I will never show this Window to the user, and I only wrap the FlowDocument with a Window so that I can see how it looks like as I develop it. This Ican live with.

So somewhere else in my application, I want to print this FlowDocument to the default printer, but I also have to set the header dynamically (in addition to many other parts of the documents that needs dynamic data that are omitted here).

The code to print looks like this:

  var printout = new ProjectPrintout();
  printout.HeaderText= new Paragraph(new Run("Proper header text"));
  var document = printout.Document;

  var pd = new PrintDialog();
  IDocumentPaginatorSource dps = document;
  pd.PrintDocument(dps.DocumentPaginator, "Document");

The document is getting printed, and looks fine except that the header text still shows "The header will go here", even if I replaced it from my code with "Proper header text". I also tried changing it like this:

(printout.HeaderText.Inlines.FirstInline as Run).Text = "Proper header text";

But the result is the same.

So the question is: How can I change the contents in the FlowDocument from code before I print it, or are there a better way to do this instead of my approach?

Best Answer

MVVM to the rescue:

Epiphany: UI is not Data. UI is not a data store. UI is meant to show Data, not to store it.

1 - Create a simple object to hold your data

public class MyDocumentViewModel: INotifyPropertyChanged //Or whatever viewmodel base class
{
    private string _header;
    public string Header 
    {
        get { return _header; }
        set
        {
            _header = value;
            NotifyPropertyChange(() => Header);
         }
     }

     //Whatever other data you need
}

2 - Define Bindings in your Document;

<Paragraph>
    <Run Text="{Binding Header}"/>
</Paragraph>

3 - Set your FlowDocument's DataContext to an instance of this class:

var flowdoc = new YourFlowDocument();
var data = new MyDocumentViewModel { Header = "this is the Header" };
//whatever other data

flowdoc.DataContext = data;

//do the printing stuff.