2
\$\begingroup\$

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
\$\endgroup\$
0
3
\$\begingroup\$

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

\$\endgroup\$
5
  • \$\begingroup\$ it should be newFixedPage.Children.Add(view) \$\endgroup\$ – Moses Aprico Nov 28 '14 at 11:46
  • \$\begingroup\$ is your answer means to shorten my code only or is it also to fix my export dispatcher priority problem? Since it seems with your code, the databinding hasn't finished loading when the exporting occurs. I think we should delay the DataContext=myViewModel until it's finished completely and and only after then it's moves to "imprint" it into the fixed page. Any idea? \$\endgroup\$ – Moses Aprico Nov 28 '14 at 11:53
  • \$\begingroup\$ well, i have no idea what caused this but, i've tried it and it didn't work. i wonder why. know other workaround? \$\endgroup\$ – Moses Aprico Nov 28 '14 at 13:42
  • \$\begingroup\$ You could try to place this Dispatcher.CurrentDispatcher.Invoke... between the blocks ( at each empty line in the for..each). \$\endgroup\$ – Heslacher Nov 28 '14 at 15:55
  • \$\begingroup\$ if you wanna know, please check my other answer which works in my case. \$\endgroup\$ – Moses Aprico Nov 28 '14 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.