Saving a collection of WPF user control layout into an XPS document

Posted on

Problem

I am making an application to save a layout of user control into an .xps document. But, the export doesn’t seem completed if the data is too many. I’ve tried to encapsulate each part with DispatcherPriority.Loaded but it seems the data binding is horrifyingly take too long to be finished (I honestly don’t know what is the problem to be exact).

How can I optimize the below code properly to wait for each process to be finished before moving on to the next one, so saving to XPS document will be properly loaded?

P.S. This doesn’t happen if the data is not too large.

public class MyClass
{
    public static void CreatePortableFile(List<MyViewModelVM> myViewModels, string path)
    {
        List<MyViewV> views = new List<MyViewV>();
        List<FixedPage> fixedPages = new List<FixedPage>();
        List<PageContent> pageContents = new List<PageContent>();
        FixedDocument fixedDoc = new FixedDocument();

        Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
            {
                foreach (MyViewModelVM item in myViewModels)
                {
                    views.Add(new MyViewV() { DataContext = item });
                }
                Console.WriteLine("Setting datacontext " + DateTime.Now.TimeOfDay);
            }), DispatcherPriority.Loaded);

        Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
            {
                foreach (MyViewV item in views)
                {
                    FixedPage newFixedPage = new FixedPage();
                    newFixedPage.Children.Add(item);
                    fixedPages.Add(newFixedPage);
                }
                Console.WriteLine("Setting fixedpage " + DateTime.Now.TimeOfDay);
            }), DispatcherPriority.Loaded);

        Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
            {
                foreach (FixedPage item in fixedPages)
                {
                    PageContent newPageContent = new PageContent();
                    ((System.Windows.Markup.IAddChild)newPageContent).AddChild(item);
                    pageContents.Add(newPageContent);
                }
                Console.WriteLine("Setting pagecontent " + DateTime.Now.TimeOfDay);
            }), DispatcherPriority.Loaded);

        Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
            {
                foreach (PageContent item in pageContents)
                {
                    fixedDoc.Pages.Add(item);
                }
                Console.WriteLine("Setting fixeddoc " + DateTime.Now.TimeOfDay);
            }), DispatcherPriority.Loaded);

        Dispatcher.CurrentDispatcher.Invoke(new Action(() => WriteToXps(path, fixedDoc)), DispatcherPriority.Loaded);
    }

    private static void WriteToXps(string path, FixedDocument fixedDoc)
    {
        XpsDocument xpsDoc = new XpsDocument(path, FileAccess.Write);
        XpsDocumentWriter xWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
        xWriter.Write(fixedDoc);
        xpsDoc.Close();

        Console.WriteLine("Setting write " + DateTime.Now.TimeOfDay);
    }
}

Sample call:

MyClass.CreatePortableFile(listOfMyViewModels, path);

Console.Writeline result:

Setting datacontext 17:51:37.8885505
Setting fixedpage 17:51:37.8915518
Setting pagecontent 17:51:37.8935523
Setting fixeddoc 17:51:37.8945527
Setting write 17:51:40.1266807

Solution

I don’t really see the sense of splitting this into different sections. Also some of them only add overhead which isn’t needed at all.

For each MyViewModelVM in List<MyViewModelVM> you are creating a MyViewV with it as DataContext which is in the next loop added as a child to a FixedPage which then is added as a child to a PageContent which is then added to the Pages of the FixedDocument.

This can be simplified to

public static void CreatePortableFile(List<MyViewModelVM> myViewModels, string path)
{

    Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
    {
        FixedDocument fixedDoc = new FixedDocument();

        foreach (MyViewModelVM item in myViewModels)
        {
            MyViewV view = new MyViewV() { DataContext = item };

            FixedPage newFixedPage = new FixedPage();
            newFixedPage.Children.Add(view);

            PageContent newPageContent = new PageContent();
            ((System.Windows.Markup.IAddChild)newPageContent).AddChild(newFixedPage);
            pageContents.Add(newPageContent);

            fixedDoc.Pages.Add(newPageContent);
        }
        WriteToXps(path, fixedDoc)

    }), DispatcherPriority.Loaded);
}  

EDIT After reading the comment, I digged a little bit more into this. As this is similiar to printing with datacontext this should fix the issue:

public static void CreatePortableFile(List<MyViewModelVM> myViewModels, string path)
{

    FixedDocument fixedDoc = new FixedDocument();

    foreach (MyViewModelVM item in myViewModels)
    {
        MyViewV view = new MyViewV() { DataContext = item };

        FixedPage newFixedPage = new FixedPage();
        newFixedPage.Children.Add(view);

        PageContent newPageContent = new PageContent();
        ((System.Windows.Markup.IAddChild)newPageContent).AddChild(newFixedPage);
        pageContents.Add(newPageContent);

        fixedDoc.Pages.Add(newPageContent);
    }

    Dispatcher.CurrentDispatcher.Invoke (new Action (delegate { }), DispatcherPriority.ApplicationIdle, null);

    WriteToXps(path, fixedDoc)
}   

A detailed explaination can be found here

Leave a Reply

Your email address will not be published. Required fields are marked *