I use PrintDialog and DocumentPaginator for printing. A.
What am I doing:
- select a printer (display the print dialog box or use the default system)
- create pages (wpf control in paper size)
- Print
Here is my test function:
public static void PrintTest1(Viewbox viewboxInWindowForRender) { FrameworkElement[] testContArr = PrepareTestContents(); //========================= PrintManager man = new PrintManager(); // Show print dialog (or select default printer) if (!man.SelectPrinter()) return; man.SetPageMargins(new Thickness(PrintManager.Size1cm * 2)); //========================= List<FrameworkElement> pagesForPrint = new List<FrameworkElement>(); for (int i = 0; i < testContArr.Length; i++) { // Put the page content into the control of the size of paper FrameworkElement whitePage = man.CreatePageWithContentStretched(testContArr[i]); // Temporary put the page into window (need for UpdateLayout) viewboxInWindowForRender.Child = whitePage; // Update and render whitePage. // Measure and Arrange will be used properly. viewboxInWindowForRender.UpdateLayout(); pagesForPrint.Add(whitePage); } viewboxInWindowForRender.Child = null; //========================= // Now you can show print preview to user. // pagesForPrint has all pages. // ... //========================= MyDocumentPaginator paginator = man.CreatePaginator(); paginator.AddPages(pagesForPrint); // Start printing man.Print(paginator, "Printing Test"); } // For testing public static FrameworkElement[] PrepareTestContents() { StackPanel sp1 = new StackPanel(); sp1.Width = PrintManager.PageSizeA4.Width - PrintManager.Size1cm * 2; sp1.Children.Add(PrepareTestBorder("Alice has a cat.")); sp1.Children.Add(PrepareTestBorder("Page number one.")); StackPanel sp2 = new StackPanel(); sp2.Width = sp1.Width / 2; sp2.Children.Add(PrepareTestBorder("Farmer has a dog.")); sp2.Children.Add(PrepareTestBorder("Page number two.")); return new FrameworkElement[] {sp1, sp2 }; } // For testing public static FrameworkElement PrepareTestBorder(string text) { Border b = new Border(); b.BorderBrush = Brushes.Black; b.BorderThickness = new Thickness(1); b.Margin = new Thickness(0, 0, 0, 5); TextBlock t = new TextBlock(); t.Text = text; b.Child = t; return b; }
Somewhere in the window, you should have a Viewbox for temporarily updating the layout and rendering.
<Window ...> <Grid> <Viewbox x:Name="forRender" Visibility="Hidden" Width="100" Height="100"/> ... </Grid> </Window>
And how you can run the test: PrintTest1(forRender);
Here is my PrintManager class:
public class PrintManager { public static readonly Size PageSizeA4 = new Size(21 * 96 / 2.54, 29.7 * 96 / 2.54); // (793.700787401575, 1122.51968503937) public static readonly double Size1cm = 96 / 2.54; // 37.7952755905512 private PrintDialog _printDialog; public PrintTicket PrintTicket { get; private set; } public PrintCapabilities TicketCapabilities { get; private set; } // Page size selected in print dialog (may not be exactly as paper size) public Size PageSize { get; private set; } public Thickness PageMargins { get; private set; } public Rect PageContentRect { get { return new Rect(PageMargins.Left, PageMargins.Top, PageSize.Width - PageMargins.Left - PageMargins.Right, PageSize.Height - PageMargins.Top - PageMargins.Bottom); } } public PrintManager() { } /// <summary> /// Show print dialog or try use default printer when useDefaultPrinter param set to true. /// <para/> /// Return false on error or when user pushed Cancel. /// </summary> public bool SelectPrinter(bool useDefaultPrinter = false) { if (_printDialog == null) _printDialog = new PrintDialog(); try { if (useDefaultPrinter) _printDialog.PrintQueue = LocalPrintServer.GetDefaultPrintQueue(); // pDialog.PrintQueue == null when default printer is not selected in system if (_printDialog.PrintQueue == null || !useDefaultPrinter) { // Show print dialog if (_printDialog.ShowDialog() != true) return false; } if (_printDialog.PrintQueue == null) throw new Exception("Printer error"); // Get default printer settings //_printDialog.PrintTicket = _printDialog.PrintQueue.DefaultPrintTicket; } catch (Exception ex) { MessageBox.Show(ex.Message); return false; } PrintTicket = _printDialog.PrintTicket; TicketCapabilities = _printDialog.PrintQueue.GetPrintCapabilities(PrintTicket); PageSize = new Size((double)TicketCapabilities.OrientedPageMediaWidth, (double)TicketCapabilities.OrientedPageMediaHeight); SetPageMargins(PageMargins); // Update margins if too small return true; } /// <summary> /// Start printing pages from paginator. /// </summary> public void Print(MyDocumentPaginator paginator, string printTaskDescription) { if (_printDialog == null) return; // Start printing document _printDialog.PrintDocument(paginator, printTaskDescription); } /// <summary> /// Set page margins and return true. /// <para/> /// If new page margins are too small (unprinted area) then set minimum and return false. /// </summary> public bool SetPageMargins(Thickness margins) { PageImageableArea pia = TicketCapabilities.PageImageableArea; PageMargins = new Thickness(Math.Max(margins.Left, pia.OriginWidth), Math.Max(margins.Top, pia.OriginHeight), Math.Max(margins.Right, PageSize.Width - pia.OriginWidth - pia.ExtentWidth), Math.Max(margins.Bottom, PageSize.Height - pia.OriginHeight - pia.ExtentHeight)); return PageMargins == margins; } /// <summary> /// Set pate margins with minimal /// </summary> public void SetMinimalPageMargins() { PageImageableArea pia = TicketCapabilities.PageImageableArea; // Set minimal page margins to bypass the unprinted area. PageMargins = new Thickness(pia.OriginWidth, pia.OriginHeight, (double)TicketCapabilities.OrientedPageMediaWidth - - pia.OriginWidth - pia.ExtentWidth, (double)TicketCapabilities.OrientedPageMediaHeight - pia.OriginHeight - pia.ExtentHeight); } /// <summary> /// Create page control witch pageContent ready to print. /// Content is stretched to the margins. /// </summary> public FrameworkElement CreatePageWithContentStretched(FrameworkElement pageContent) { // Place the content inside the page (without margins) Viewbox pageInner = new Viewbox(); pageInner.VerticalAlignment = VerticalAlignment.Top; // From the upper edge pageInner.Child = pageContent; // Printed control - the page with content Border whitePage = new Border(); whitePage.Width = PageSize.Width; whitePage.Height = PageSize.Height; whitePage.Padding = PageMargins; whitePage.Child = pageInner; return whitePage; } /// <summary> /// Create page control witch pageContent ready to print. /// <para/> /// Content is aligned to the top-center and must have /// a fixed size (max PageSize-PageMargins). /// </summary> public FrameworkElement CreatePageWithContentSpecSize(FrameworkElement contentSpecSize) { // Place the content inside the page Decorator pageInner = new Decorator(); pageInner.HorizontalAlignment = HorizontalAlignment.Center; pageInner.VerticalAlignment = VerticalAlignment.Top; pageInner.Child = contentSpecSize; // Printed control - the page with content Border whitePage = new Border(); whitePage.Width = PageSize.Width; whitePage.Height = PageSize.Height; // We align to the top-center only, because padding will cut controls whitePage.Padding = new Thickness(0, PageMargins.Top, 0, 0); whitePage.Child = pageInner; return whitePage; } /// <summary> /// Create paginator for pages created by CreatePageWithContent(). /// </summary> public MyDocumentPaginator CreatePaginator() { return new MyDocumentPaginator(PageSize); } }
And here is my MyDocumentPaginator class:
public class MyDocumentPaginator : DocumentPaginator { private List<FrameworkElement> _pages = new List<FrameworkElement>(); public override bool IsPageCountValid { get { return true; } } public override int PageCount { get { return _pages.Count; } } public override Size PageSize { get; set; } public override IDocumentPaginatorSource Source { get { return null; } } public MyDocumentPaginator(Size pageSize) { PageSize = pageSize; } public override DocumentPage GetPage(int pageNumber) {
You told me you want to print the controls you already have.
You can print this with my solution.
For instance:
Suppose you have a UserCtrl that is in a ParentBorder . You need to remove it from the parent control, and then you can use it.
ParentBorder.Child = null;
How can you prepare the page:
FrameworkElement whitePage = man.CreatePageWithContentStretched(UserCtrl); viewboxInWindowForRender.Child = whitePage; viewboxInWindowForRender.UpdateLayout(); MyDocumentPaginator paginator = man.CreatePaginator(); paginator.AddPages(whitePage); man.Print(paginator, "Printing UserControl");
Here is the RemoveFromParent function:
public static void RemoveFromParent(FrameworkElement child) { DependencyObject parent = child.Parent; if (parent == null) return; if (parent is Panel) ((Panel)parent).Children.Remove(child); else if (parent is Decorator) ((Decorator)parent).Child = null; else if (parent is ContentControl) ((ContentControl)parent).Content = null; else if (parent is ContentPresenter) ((ContentPresenter)parent).Content = null; else throw new Exception("RemoveFromParent: Unsupported type " + parent.GetType().ToString()); }
Why am I using UpdateLayout and Viewbox in a window instead of Measure and Arrange like the people in other examples?
I try, but I had a lot of problems with this. I use the controls that I have, I change the print style, as well as export to PDF . Measure and order do not work for me. The controls must be docked in the window to properly update and render the layout.