WPF Visual Control Printing

Source: Internet
Author: User

Introduction

While coding-an application-displays a detailed report ScrollViewer in a, it is decided that it would is nice to print The report to a printer.

I found That wpf provides a  printdialog. printvisual  method for printing any WPF  control derived from the  Visual  class.   will only print a Scale your control-to-fit on the page. Unfortunately this would is sometimes a long enough that it could isn't being read easily when Scaled to fit on the page.

Another option For printing provided by  wpf is to create a separate view in a  FlowDocument . The best-of-the-To print documents probably, but it's more work than I WIS Hed to put in it, not to mention the extra view that would has to is maintained for each control I wished To print.

What am I ended up doing the a bit unorthodox but works well for my purpose of the printing a report it is already displ Ayed in the application. I take the control and convert it to a bitmap that'll look good on a printer and then chop the bitmap up I Nto pieces that would fit on a page, add the pages to a and send this to the FixedDocument printer using PrintDialog.PrintDocument .

Using the Code

Below is a class so can bind to that would print any control derived from the FrameworkElement class.

Hide Shrink Copy Code
PublicClassprintcommand:icommand{PublicBOOL CanExecute (Object parameter) {ReturnTrue }Publicvoid Execute (Object parameter) {if (parameteris FrameworkElement) {FrameworkElement ObjecttoPrint = parameterAs FrameworkElement;PrintDialogPrintDialog =NewPrintDialog ();if ((boolPrintdialog.showdialog (). GetValueOrDefault ()) {mouse.overridecursor = cursors.wait; System.Printing.PrintCapabilities capabilities =PrintDialog.Printqueue.getPrintCapabilities (PrintDialog.PrintTicket);Double Dpiscale =300.0/96.0; FixedDocument document =New FixedDocument ();try {//The layout of the UI Control to match the width of thePrinter page ObjecttoPrint.width = capabilities. Pageimageablearea.extentwidth; ObjecttoPrint.updatelayout (); ObjecttoPrint.measure (New Size (Double. PositiveInfinity,Double. PositiveInfinity)); Size size =New Size (capabilities. Pageimageablearea.extentwidth, ObjecttoPrint.DesiredSize.Height); ObjecttoPrint.measure (size); Size =New Size (capabilities. Pageimageablearea.extentwidth, ObjecttoPrint.DesiredSize.Height); ObjecttoPrint.measure (size); ObjecttoPrint.arrange (New Rect (size));//Convert the UI control into a bitmap at + dpiDouble Dpix =300;Double Dpiy =300; RenderTargetBitmap BMP =New RenderTargetBitmap (Convert.ToInt32 (capabilities. Pageimageablearea.extentwidth * Dpiscale), Convert.ToInt32 (ObjecttoPrint.actualheight * Dpiscale), Dpix, Dpiy, pixelformats.pbgra32); Bmp. Render (ObjecttoPrint);//Convert the RenderTargetBitmap into a bitmap we can further readily use pngbitmapencoder PNG =New Pngbitmapencoder (); Png. Frames.add (Bitmapframe.create (BMP)); System.Drawing.Bitmap BMP2;using (MemoryStream MemoryStream =New MemoryStream ()) {png. Save (MemoryStream); BMP2 =New System.Drawing.Bitmap (MemoryStream); } document. Documentpaginator.pagesize =New Size (PrintDialog.Printableareawidth,PrintDialog.Printableareaheight);//Break the bitmap down into pagesint pagebreak =0;int previouspagebreak =0;int pageheight = Convert.ToInt32 (capabilities. Pageimageablearea.extentheight * Dpiscale);while (PageBreak < BMP2. Height-pageheight) {pagebreak + = PageHeight;//Where we thing the end of the page should be//Keep moving up a row until we find a good place-to-break the pagewhile (! Isrowgoodbreakingpoint (BMP2, pagebreak)) pagebreak--; PageContent pagecontent = generatepagecontent (BMP2, Previouspagebreak, PageBreak, document. DocumentPaginator.PageSize.Width, document. DocumentPaginator.PageSize.Height, capabilities); Document. Pages.Add (PageContent); Previouspagebreak = PageBreak; }//Last Page pagecontent lastpagecontent = generatepagecontent (BMP2, Previouspagebreak, BMP2. Height, document. DocumentPaginator.PageSize.Width, document. DocumentPaginator.PageSize.Height, capabilities); Document. Pages.Add (lastpagecontent); }finally {//Scale UI control back to the original so we don ' t effect what's on the screen ObjecttoPrint.width =Double. NaN; ObjecttoPrint.updatelayout (); Objecttoprint.layouttransform = new ScaleTransform (1, 1); Size size = new size (capabilities. Pageimageablearea.extentwidth, capabilities. Pageimageablearea.extentheight); Objecttoprint.measure (size); Objecttonew Rect (new point (capabilities. Pageimageablearea.originwidth, capabilities. pageimageablearea.originheight), size); Mouse.overridecursor = null;} printdialog. printdocument (document. DocumentPaginator,  "Print Document Name "); } } }

The GeneratePageContent method creates one page from a section of the bitmap of the UI control. The content on the page would show everything from (the first row of the page) to (the last row of the top  bottom  page.) You could modify this method to the add a header and/or footer to each page if desired.

Hide Shrink Copy Code
Private PageContent generatepagecontent (System.Drawing.Bitmap bmp,int top,int bottom,Double PageWidth,Double PageHeight, System.Printing.PrintCapabilities capabilities) {FixedPagePrintdocumentpage =New FixedPage ();Printdocumentpage.width = PageWidth;Printdocumentpage.height = PageHeight;int newimageheight = Bottom-top; System.Drawing.Bitmap bmppage = bmp. Clone (New System.Drawing.Rectangle (0, top, BMP. Width, Newimageheight), SYSTEM.DRAWING.IMAGING.PIXELFORMAT.FORMAT32BPPARGB);//Create a new bitmap for the contents of this page Image pageimage =New Image (); BitmapSource Bmpsource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap (Bmppage.gethbitmap (),intptr.zero, System.Windows.Int32Rect.Empty, Bitmapsizeoptions.fromwidthandheight ( Bmp. Width, Newimageheight)); Pageimage.source = Bmpsource; Pageimage.verticalalignment = Verticalalignment.top; // Place the bitmap on the page  PRINTDOCUMENTPAGE.CHILDREN.ADD (Pageimage); PageContent pagecontent = new pagecontent (); ((System.Windows.Markup.IAddChild) pagecontent). AddChild (printdocumentpage); Fixedpage.setleft (Pageimage, capabilities. Pageimageablearea.originwidth); Fixedpage.settop (Pageimage, capabilities. Pageimageablearea.originheight); Pageimage.width = capabilities. Pageimageablearea.extentwidth; Pageimage.height = capabilities. Pageimageablearea.extentheight; return pagecontent;}       

The IsRowGoodBreakingPoint method evaluates a row of the bitmap to determine if it was a good place to start a new page. This was a bit magical, but basically if the values of pixels in the row vary in color values to much, then there must be t Ext or something else there so we don ' t want to break to another page there. maxDeviationForEmptyLine the variable is basically a tolerance value that would allow some deviation for table borders, etc.

Hide Copy Code
int row) {    1627500;    return goodbreakingpoint;} 

The method below is used to calculate how much difference there are in the colors of the pixels one rowPixelDeviation row of th E bitmap. This method uses pointers to quickly go through the bitmap, so you'll have the set of the Allow unsafe code prope Rty for the project.

Hide Shrink Copy Code
PrivateDouble rowpixeldeviation (System.Drawing.Bitmap bmp,int row) {int count =0;Double total =0;Double totalvariance =0;Double standarddeviation =0; System.Drawing.Imaging.BitmapData bmpdata = bmp. LockBits (New System.Drawing.Rectangle (0,0, BMP. Width, BMP. Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, BMP. PixelFormat);int stride = Bmpdata.stride;IntPtr firstpixelinimage = bmpdata.scan0;unsafe {byte* p = (byte*) (void*) firstpixelinimage; p + = stride * ROW;//Find starting pixel of the specified rowfor (int column = 0; column < BMP. Width; column++) {count++; count the pixels byte blue = P[0]; byte green = P[1"; byte red = P[3]; int pixelvalue = System.Drawing.Color.FromArgb (0, red, green, Blue). ToArgb (); Total + = Pixelvalue; double average = total/count; Totalvariance + = Math.pow (pixelvalue-average, 2); Standarddeviation = Math.sqrt (Totalvariance/count); // go to next pixel p + = 3;}} bmp. Unlockbits (Bmpdata); return standarddeviation;}          

As mentioned at the beginning of the article, this is developed for printing UI controls that display some sort of re Port or details. It is not going to work in it present state if the control contains an image as a background or contains an image that en DS up being larger than what would fit on a page vertically.

WPF Visual Control Printing

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.