Failed to correctly exports wpf view to xps if the data is "too many"

421 views Asked by At

THE PROBLEM


So, I am building my application so it's view can be exported into and Xps document. The application is literally just an ItemsControl of a UserControl.

It works fine until I add some more data to the ItemsControl, the exported file is just shows an "unfinished" loaded view.

This is my export-to-xps code :

    public static void CreatePortableFile(List<N_SheetPageVM> numberedSheetPages, string path)
    {
        List<N_SheetPageV> sheetPages = new List<N_SheetPageV>();
        foreach (N_SheetPageVM item in numberedSheetPages)
        {
            N_SheetPageV temp = new N_SheetPageV();
            temp.DataContext = item;
            sheetPages.Add(temp);
        }

        List<FixedPage> fixedPages = new List<FixedPage>();
        foreach (N_SheetPageV item in sheetPages)
        {
            FixedPage newFixedPage = new FixedPage();
            newFixedPage.Children.Add(item);
            fixedPages.Add(newFixedPage);
        }

        List<PageContent> pageContents = new List<PageContent>();
        foreach (FixedPage item in fixedPages)
        {
            PageContent newPageContent = new PageContent();
            ((System.Windows.Markup.IAddChild)newPageContent).AddChild(item);
            pageContents.Add(newPageContent);
        }

        FixedDocument fixedDoc = new FixedDocument();
        foreach (PageContent item in pageContents)
        {
            fixedDoc.Pages.Add(item);
        }

        XpsDocument xpsDoc = new XpsDocument(path, FileAccess.Write);
        XpsDocumentWriter xWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
        xWriter.Write(fixedDoc);
        xpsDoc.Close();
    }

    public static void CreatePortableFile(List<S_SheetPageVM> stavedSheetPages, string path)
    {
        List<S_SheetPageV> sheetPages = new List<S_SheetPageV>();
        foreach (S_SheetPageVM item in stavedSheetPages)
        {
            sheetPages.Add(new S_SheetPageV() { DataContext = item });
        }

        List<FixedPage> fixedPages = new List<FixedPage>();
        foreach (S_SheetPageV item in sheetPages)
        {
            FixedPage newFixedPage = new FixedPage();
            newFixedPage.Children.Add(item);
            fixedPages.Add(newFixedPage);
        }

        List<PageContent> pageContents = new List<PageContent>();
        foreach (FixedPage item in fixedPages)
        {
            PageContent newPageContent = new PageContent();
            ((System.Windows.Markup.IAddChild)newPageContent).AddChild(item);
            pageContents.Add(newPageContent);
        }

        FixedDocument fixedDoc = new FixedDocument();
        foreach (PageContent item in pageContents)
        {
            fixedDoc.Pages.Add(item);
        }

        XpsDocument xpsDoc = new XpsDocument(path, FileAccess.Write);
        XpsDocumentWriter xWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
        xWriter.Write(fixedDoc);
        xpsDoc.Close();
    }

SCREENSHOTS


1. SMALL DATA SCREENSHOT Below screenshot shows that if the data is small, then it's correctly exported into xps :

enter image description here

2. UNEXPECTED LARGE DATA SCREENSHOT Below screenshot shows that if the data is "large", then it's incorrectly exported into xps :

enter image description here

3. EXPECTED LARGE DATA SCREENSHOOT (taken pre-export) This is the expected output of the "large" data (screenshot taken from the application, pre-export, since the export failed to behave correctly)

enter image description here

ADDITIONAL DETAIL


  1. Actually, even if it's just a normal "LOAD" at the application, the first thing shown on the screen is screenshot #2 but then, #3 is shown. That's why I guess it's the loading which is the problem.
  2. I've checked the loading method (which is 1 for each class), which is : Clearing the viewmodel data and then reassigning the model into it's viewmodel. And the number it's called is correct, so, no duplicate calls.
  3. If you need to see it yourself, then I've provided the download link to both unexpected and expected input and output files, as well as the .exe here.
  4. Why I quote "too many" or "many"? It's because I think it's too many.
  5. There's no async method.
  6. You can request the actual C# project if it's needed.
  7. The page arrangement is manually done by the user (add or remove new page after the last), which handled by a collection of somewhat similar to PageVM.
  8. Full MVVM approach IS NOT REQUIRED.

WHAT I PREDICT THE SOLUTION WILL LOOK LIKE


Maybe the solution will somewhat to delay the export process to wait for the load to be finished (even though they're all actually synchronous processes, no async in this project)

P.S. If you need to full source code, I will gladly share it to you in a private room.

Thank you.

2

There are 2 answers

1
Mike Strobel On BEST ANSWER

I noticed you are setting the DataContext on the items you are placing in your document. This suggests that the content contains bindings, in which case the problem is likely that the binding targets have not yet been updated at the time you export the document.

Data bindings are processed at a DispatcherPriority of DataBind (level 8). In order to ensure that the data is actually available when you export your document, you need to schedule the export to occur after the bindings are processed. You also want to make sure your content has undergone layout. You can accomplish both of these by scheduling the export to occur at DispatcherPriority.Loaded (level 6), which runs after DataBind and at the same priority as layout.

Try updating your export methods as follows:

List<N_SheetPageV> sheetPages = new List<N_SheetPageV>();
foreach (N_SheetPageVM item in numberedSheetPages)
{
    N_SheetPageV temp = new N_SheetPageV();
    temp.DataContext = item;
    sheetPages.Add(temp);
}

...

Dispatcher.CurrentDispatcher.Invoke(
    DispatcherPriority.Loaded,
    new Action(
        () =>
        {
            XpsDocument xpsDoc = new XpsDocument(path, FileAccess.Write);
            XpsDocumentWriter xWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
            xWriter.Write(fixedDoc);
            xpsDoc.Close();
        })
    );
0
Moses Aprico On

If anyone experienced the same problem as mine, please use this, this solved my problem :

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

    FixedDocument fixedDoc = new FixedDocument();

    foreach (MyViewModelVM item in myViewModels)
    {
        //idem
    }

    DocumentViewer dummy = new DocumentViewer(); //it's the key
    dummy.Document = fixedDoc; //it's the key

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

    WriteToXps(path, fixedDoc)
}   

hope it helps someone out there. (If someone can explain this behavior, it would be great since I don't)