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
1 Answer 1
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
-
\$\begingroup\$ it should be
newFixedPage.Children.Add(view)
\$\endgroup\$Moses Aprico– Moses Aprico2014年11月28日 11:46:20 +00:00Commented Nov 28, 2014 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– Moses Aprico2014年11月28日 11:53:33 +00:00Commented Nov 28, 2014 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– Moses Aprico2014年11月28日 13:42:42 +00:00Commented Nov 28, 2014 at 13:42
-
\$\begingroup\$ You could try to place this
Dispatcher.CurrentDispatcher.Invoke...
between the blocks ( at each empty line in thefor..each
). \$\endgroup\$Heslacher– Heslacher2014年11月28日 15:55:50 +00:00Commented Nov 28, 2014 at 15:55 -
\$\begingroup\$ if you wanna know, please check my other answer which works in my case. \$\endgroup\$Moses Aprico– Moses Aprico2014年11月28日 17:39:52 +00:00Commented Nov 28, 2014 at 17:39