This project is read-only.

How to create FlowDocumentEx with repeating section outside of a WPF application

Jul 2, 2014 at 4:44 PM
Hello,

What I would like to do is generate an XPS report from data that is currently in a Winforms application. I would like to take advantage of some of the features in the Code Framework's FlowDocumentEx class to do it. I have the following code that almost works.
   var data = new InvoiceModel()
   var xamlContents = File.ReadAllText("Invoice.xaml");
   var reader = new StringReader(xamlContents);
   var xReader = XmlReader.Create(reader);
   FlowDocumentEx doc = (FlowDocumentEx)XamlReader.Load(xReader);
   xReader.Close();
   reader.Close();
   doc.DataContext = data;
   Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.SystemIdle, new
      DispatcherOperationCallback(o => null), null);
   const string STR_DTempTestFilexps = @"d:\Temp\TestFile.xps";
   doc.SaveAsXps(STR_DTempTestFilexps);
   Process.Start(STR_DTempTestFilexps);
Here is a simplified example XAML file.
<doc:FlowDocumentEx xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    xmlns:doc="clr-namespace:CODE.Framework.Wpf.Documents;assembly=CODE.Framework.Wpf.Documents"
                    Title="Invoice" FontFamily="Segoe UI" FontSize="10pt" ColumnWidth="800">
    <Paragraph TextAlignment="Right"><Run Text="{Binding InvoiceDate}"></Run></Paragraph>
    <doc:RepeatingSection ItemsSource="{Binding LineItems}">
        <doc:RepeatingSection.ItemTemplate>
            <doc:DocumentDataTemplate>
                <Paragraph>
                    <Run Text="{Binding Path=.}"></Run>
                </Paragraph>
            </doc:DocumentDataTemplate>
        </doc:RepeatingSection.ItemTemplate>
    </doc:RepeatingSection>
</doc:FlowDocumentEx>
Starting with the example project from this article http://www.codemag.com/Article/1304041 I was able to get everything to work if I run it in the context of a WPF application.

However I just want to take the XAML file and then bind it to some data and generate the XPS file. So using the above code everything works except for the repeating sections. They are always blank. The rest of the document and the databinding works fine. Does anyone know how I could get the repeating sections to work? I am fairly new to XAML and WPF so I hope it is something simple I am missing.

Thanks,
Jonathan
Jul 7, 2014 at 11:04 AM
What do you mean by "running it outside the context of a WPF application?". My guess would be you want to run it on a server somewhere to do server side document creation?

There are several difficulties with that approach. For one, the bindings do not update until the windows message loop kicks in. I find that to be a very serious issue, since it is very difficult to make that happen on a server without a logged in user. I do not have a good answer to that.

Also, if you do this as part of a service (or possibly a web app... I am not totally sure of all the technical implications of a web app, but I think they are similar to services/WCF) you have the difficulty that it isn't the right threading model,. You in essence need to force the app into an STA model. That is a bit of a hack, but at least with WCF it is doable. In fact, CODE Framework (under the CODE.Framework.Services.Server namespace) has an [STAOperation] attribute you can put on a WCF service operation (method), which forces the execution of that operation to follow STA threading. In fact, we are using that very technique on www.Xamalot.com to use WPF to create graphics from XAML on the fly. It works well for that purpose, but I think you will still have issues with binding not necessarily refreshing properly before you are done printing, and hence end up with empty blocks in the document.


Markus
Jul 7, 2014 at 10:36 PM
Markus,

Thank you very much for your detailed reply. What I am trying to do is generate a report from a Winforms application. That is what I meant by outside the context of a WPF application. However as you stated I am having problems with the binding not working right. I have found an alternative way to do what I need to do so for now I won't need to use the CODE Framework (at least for this application). Thanks again anyway for your answer.

Jonathan
Jul 7, 2014 at 10:39 PM
Ah, I see. Actually, in a WinForms app you are facing an easier scenario. You still need to get the WPF elements to realize they need to refresh properly, which means the message pump has to kick in. You could do this by opening a WPF window from your WinForms app that hosts a preview of the report (I would do it at a position way off screen, so users can't see it). This way, things are forced to refresh, and everything should show up properly. You can do that with or without a CODE Framework. shell window. You could just load the doc and load a document viewer (plain WPF) into that window, and you should be good to go.


Markus