How to combine multiple PrintVisual() in 1 print-job? - c#

I have a C# WPF application (.NET 4.6) that needs to print a number of Page objects.
Each Page is setup in the XAML editor to be exactly 1 page of A4 in size and the content is build from a number of label, border and image components placed on the Page.
I added a method to my Page class that adds some dynamic data at run-time to the Page and then prints it using PrintVisual().
In fact: Its a loop that fills a Page object with the dynamic content and then calls PrintVisual() to print it. For each iteration, some new content is placed in the Page. (See sample code.)
// Print method of the Page object.
public void PrintIt(int totalpages)
{
PrintDialog pDialog = new PrintDialog();
// If not cancelled then print
if (pDialog.ShowDialog() == false) return; // User cancelled. Don't bother
for(int pagenum=1; i<=totalpages; i++)
{
// Omitted several dozen statements that fill/update the content of various
// label components based on the pagenumber.
this.UpdateLayout(); // Required to re-render new page properly before printing.
pDialog.PrintVisual(this, "My PrintJob");
}
}
This works well, expect for the fact that each PrintVisual() creates a separate print-job, which makes it impossible to print double-sided (and when printing to PDF each page ends up in a new file).
I need to tie all pages (PrintVisual() calls) in a single print-job. I'm fairly sure that I need to use a PrintDocument(), but I can't seem to figure out how to construct the required FlowDocument and populate it with my actual prints.
I'm quite new to WPF programming so I'm probably missing something obvious, but as far as I can't tell there isn't any simple way to do this.
Can anyone give me a push in the right direction ?

Related

How to execute script using webview2 to expand an html element using c#

I'm not quite sure how to ask this question. So I'm attaching two images
How can I expand the Product Details tab so that the html rendered in the webview2 control looks like the second picture using code behind?
You can inject script into a page using CoreWebView2.ExecuteScriptAsync that will find that element and perform a click on it. However, this will be a fragile solution since if the layout of the page or names of elements on the page change your script may no longer work.
I would recommend opening the desired page in the Edge browser, opening DevTools, Console, and trying out different script to click on the correct element. For example document.getElementById("product-section-overview").children[0].children[0].click() might work but depends on the ID of that element not changing, and the structure and order of its children.
Once you have the script you want you can inject it into the document via
await myWebView2.CoreWebView2.ExecuteScriptAsync("document.getElementById("product-section-overview").children[0].children[0].click()");
If you are interested in a library for use with WebView2 that provides a DOM API and automation functionality then you can try WebView2.DevTools.Dom. It's free for anyone to use.
// Add using WebView2.DevTools.Dom; to access the CreateDevToolsContextAsync extension method
// Only a single WebView2DevToolsContext should exist at any given time, when you are finished them make sure you dispose via DisposeAsync. The WebView2DevToolsContext can be reused if required
await using var devToolsContext = await webView.CoreWebView2.CreateDevToolsContextAsync();
// Get element by Id
// https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
var element = await devToolsContext.QuerySelectorAsync<HtmlElement>("#myElementId");
// Click The element
// This will simulate a mouse move then click
await element.ClickAsync();
More details and examples in the Readme

How can I insert an element to the bottom of a specific page in iText7?

I'm exploring different options for .NET PDF libraries. One of my requirements is to place a box at the bottom of the first page and if any of the content reaches the box, it should overflow onto the next page.
For example:
Shown above, paragraph 7 would normally take up some of the space that's occupied by the "reserved" area. Instead, the part that would have taken up that space is shifted to the next page.
That image was achieved using Gembox.Document by adding the box as a footer element that only renders on the first page. However, in iText7, the examples I've seen for adding a footer (such as this one), places the content as a floating element that renders over the existing content and does not affect the layout/flow of the rest of the document.
I also tried adding a paragraph on the PageEnd event handler without the canvas (snippet below), but instead of adding it to the specified page, it's added to the end of the entire document.
public void HandleEvent(Event evt)
{
var docEvent = (PdfDocumentEvent)evt;
var page = docEvent.GetPage();
int pageNum = docEvent.GetDocument().GetPageNumber(page);
if (pageNum == 1)
{
doc.Add(new Paragraph("Testing a thing"));
}
}
Is the type of effect I'm looking for something that I can replicate using iText7?
I believe you can combine the concepts of https://github.com/itext/i7ns-samples/blob/develop/itext/itext.samples/itext/samples/sandbox/acroforms/AddExtraTable.cs and https://github.com/itext/i7ns-samples/blob/develop/itext/itext.samples/itext/samples/sandbox/events/TextFooter.cs to achieve what you need.
The idea is as follows:
reserve place for your box by making iText give the document's renderer less space for the first page
fill this box with a help of iText's end page events
Another option was suggested in How can I insert an element to the bottom of a specific page in iText7? : you can temporary call Document#setBottomMargin , since elements added via Document#add will not be placed on margins. Then, once the first page is layouted, you can set the initial margins again. This option, however, requires understanding of you layout flow, since the margins should be set only after the content of the first page is layouted.
One more suggestion: althouth event functionality is rather flexible and useful, it seems like using a sledgehammer to crack a nut. You need to call Canvas#ShowTextAligned, which could be done without any event handling. So ideally I would prefer to do the following:
handle page's layout area via an extension of DocumentRenderer
Calling Canvas#ShowTextAligned to fill the reserved box.
As you said, you are exploring different .NET PDF libraries. So I would advise PDFFlow library, which does exactly what you need.
If you have a footer, main document flow will take the rest of page area and will be automatically continued at the next page without overlaying footer.
DocumentBuilder.New()
.AddSection()
.AddParagraph("long text")
.ToSection()
.AddFooterToBothPages(40)
.AddParagraph("this a footer set for each page of this section")
.ToDocument()
.Build("result.pdf");
Here is a tutorial with code examples of using headers, footers, left/right repeating areas: AddingRepeatingArea tutorial.
Hope, this will help you :)

Printing Grid Control with multiple elements

I am trying to print a grid control with multiple elements in it.
I am doing this,
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
printDialog.PrintVisual(gridReport, "Visit Report");
}
gridReport is my grid name.
As my grid contains a lot of child elements, only a part of it is getting printed and rest is getting chopped.
How should i solve this ?
I had the same problem a month ago. As far as i know .Net print dialog only prints one page and cuts everything beyond that page. If you want to print multi pages you have to write some logic yourself.
I created a bmp file and cut it to multiple pages, added the pages to a list and at the end printed the list of pages.
I found this article very helpful (it has a solution that cuts the bmpwhenever the height of one page is exceeded, so you will also have to implement a similar logic to cut your bmp when the width of a page is exceeded)
http://www.codeproject.com/Articles/339416/Printing-large-WPF-UserControls
i hope this will help.

XFA Creating Multiple Instances of a Page with iTextSharp

Looking at the USCIS form N-400, you can see that in Part 9 has two buttons, Add Children and Go to continuation page Looking at the XML/JS backing those buttons you get this node for the corresponding field:
<event activity="click" name="event__click">
<script contentType="application/x-javascript">
_KidsContPage.addInstance(1);
xfa.form.recalculate(1);
xfa.host.pageDown( );
</script>
</event>
This is used to add the automatically hidden page to the form in case the user has more than 8 children. My question is using iTextSharp, or perhaps some other method, how do I create this new instance of the page? If you notice you can make multiple copies of this additional children page, if, somehow, you happen to have more than 25 kids, so not getting side-tracked by the unlikelihood of that possibility, I need to know how to create multiples of such a page...
This is what I have so far:
PdfAction cloneAction = PdfAction
.JavaScript(ClonePage("KidsContPage"), stamper.Writer);
stamper.Writer.AddJavaScript(cloneAction);
I've also tried with SetOpenAction
stamper.Writer.SetOpenAction(cloneAction);
private string ClonePage(string formName)
{
return #"
if (xfa.host.name != 'XFAPresentationAgent') {
$._" + formName + #".addInstance(1);
if (xfa.host.version < 8) {
xfa.form.recalculate(1);
}
}";
}
I know that my ClonePage() code is being run, because I see the alert when I tested it earlier, the problem is in the javascript or perhaps I somehow need to run it at server or who knows what I need to do. I opened the XFA PDF in LiveCycle and that's the JS that it's putting out for it, I must be missing something small somewhere...and it works fine in LiveCycle. Please help.

Why does Print Preview show properly formatted pages that won't actually print?

I am writing an app to print formatted data using Visual Studio 2008/C#. I have formatted the data in the fashion that I want it to display. I am using two Print Documents and event handlers, because the first page of the report carries formatting requirements that differs from pages 2 through N.
Print Preview shows me properly formatted data for all pages that I try to print. Nevertheless, pages 2 through N will not actually print.
I've stepped through my code and the data is being passed correctly to the event handler. This is the block of code that calls the second print document's event handler. What am I doing wrong?
// First page print limit has been reached. Do we
// still have unprinted items in the arraylist? Call the second
// print handler event and print those items.
if (((alItemsToPrint.Count) - iItemPrintedCount) > 0)
{
// Getting a look at my formating
PrintPreviewDialog printPreview2 = new PrintPreviewDialog();
printPreview2.Document = ItemsPrintDocument;
printPreview2.ShowDialog();
printPreview2.Dispose();
// Print item overflow pages
ItemsPrintDocument.Print();
// Release the resources consumed by this print document
ItemsPrintDocument.Dispose();
}
Thanks for your time, everyone.
To Print a Document, you use:
PrintDocument.Print
When Preview, You assign The PrintDocument to PrintPreviewDialog
printPreview2.Document = ItemsPrintDocument;
When You show PrintPreviewDialog, it replaces the PrintDocument' PrintController to PreviewPrintController and call PrintDocument.Print.
This action generates a List of images (metafiles) one on each page.
Next, it restore the original PrintController on PrintDocument and show images.
When You press the PrintButton on PrintPreviewDialog, it calls PrintDocument.Print with original PrintController.
Note that for correct behavior you can use BeginPrint' PrintDocument event to initialize vars to new PrintDocument.Print.
If you use PrintPreviewDialog, you do´nt need call PrintDocument.Print.

Categories