WPF Printing to fit page

18,331

Solution 1

I know this question is quite old but looking for a solution to this problem myself. Here is the solution I am currently using. I store the original transformation against the framework element and then reapply it after the printing has finished.

    private void Print(Visual v)
    {

        System.Windows.FrameworkElement e = v as System.Windows.FrameworkElement ;
        if (e == null)
            return;

        PrintDialog pd = new PrintDialog();
        if (pd.ShowDialog() == true)
        {
            //store original scale
            Transform originalScale = e.LayoutTransform;
            //get selected printer capabilities
            System.Printing.PrintCapabilities capabilities = pd.PrintQueue.GetPrintCapabilities(pd.PrintTicket);

            //get scale of the print wrt to screen of WPF visual
            double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / e.ActualWidth, capabilities.PageImageableArea.ExtentHeight /
                           e.ActualHeight);

            //Transform the Visual to scale
            e.LayoutTransform = new ScaleTransform(scale, scale);

            //get the size of the printer page
            System.Windows.Size sz = new System.Windows.Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);

            //update the layout of the visual to the printer page size.
            e.Measure(sz);
            e.Arrange(new System.Windows.Rect(new System.Windows.Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));

            //now print the visual to printer to fit on the one page.
            pd.PrintVisual(v, "My Print");

            //apply the original transform.
            e.LayoutTransform = originalScale;
        }
    }

Solution 2

    private void DrawGrap_Click(object sender, RoutedEventArgs e)
    {
        // Visual v = sender as Visual;
        Visual v = song2Grid as Visual;   // the object (it is a DataGrid) that you want to print out, not a window
        PrintDialog prtDlg = new PrintDialog();
        if (prtDlg.ShowDialog() == true)
        {
            // because 96 pixels in an inch for WPF window
            double marginLeft = 96.0 * 0.75; // left margin is 0.75 inches
            double marginTop = 96.0 * 0.75; // top margin is 0.75 inches
            double marginRight = 96.0 * 0.75; // right margin is 0.75 inches
            double marginBottom = 96.0 * 0.75; // bottom margin is 0.75 inches

            // the following steps do not works for a WPF window
            FrameworkElement win = v as FrameworkElement;
            Transform oldLayoutTransform = win.LayoutTransform;
            Size oldSize = new Size(win.ActualWidth, win.ActualHeight);

            System.Printing.PrintCapabilities pCapability = prtDlg.PrintQueue.GetPrintCapabilities(prtDlg.PrintTicket);

            // calculate print area that you want
            double printWidth = (pCapability.PageImageableArea.ExtentWidth - pCapability.PageImageableArea.OriginWidth)
                                - (marginLeft + marginRight);
            double printHeight = (pCapability.PageImageableArea.ExtentHeight - pCapability.PageImageableArea.OriginHeight)
                - (marginTop + marginBottom);

            // calculate the scale
            double scale = Math.Min(printWidth / oldSize.Width , printHeight / oldSize.Height);
            if (scale > 1.0)
            {
                // keep the original size and layout if printable area is greater than the object being printed
                scale = 1.0; 
            }

            // store the original layouttransform
            win.LayoutTransform = new ScaleTransform(scale, scale);

            // new size of the visual
            Size newSize = new Size(oldSize.Width*scale , oldSize.Height*scale);
            win.Measure(newSize);

            // centralize print area
            double xStartPrint = marginLeft + (printWidth - newSize.Width)/2.0;
            double yStartPrint = marginTop + (printHeight - newSize.Height)/2.0;
            win.Arrange(new Rect(new Point(xStartPrint,yStartPrint),newSize));

            // print out the visual
            prtDlg.PrintVisual(win as Visual, "PrintTest");

            // resotre the original layouttransform and size on screen
            win.LayoutTransform = oldLayoutTransform;
            win.Measure(oldSize);
            win.Arrange(new Rect(new Point(0,0),oldSize));
        }
    }

It was an answer to the question that was asked by user1018711. Fitting a print out on one printer page using C# and WPF. When you want to print out a visual, which might be a control what may including a lot of controls (e.g. Button, DataGrid, TextBlock, Label ,and etc). Here I want to print a DataGrid named song2Drid to printer but its content was larger than page size of the printer (its width was wider than width of a paper) so it was truncated. I could not see all of them so I had to scale the visual but I wanted to keep the ratio just as the same as the old one.

I also set paper margins to 0.75 inches to each side of paper, which left, top, right, and bottom. I also centralized the content of visual (song2Grid) on the paper. So I could see the printed content just on the center of the paper. But if the visual was a window like Application.Current.MainWindow or any window created programmatically by new Window(), then It will not be scaled. it meant this method wont work for a Window object.

Also, If you want to recover the original look on screen from the changed one by scaling to print, then you have to have the statements as the following win.LayoutTransform = oldLayoutTransform; win.Measure(oldSize); win.Arrange(new Rect(new Point(0,0),oldSize));

Share:
18,331
Admin
Author by

Admin

Updated on July 22, 2022

Comments

  • Admin
    Admin almost 2 years

    i searched for options how to print WPF controls and found some solutions. I do need to fit my printed control to printing page while preserving aspect ration (my control is square; sudoku grid).

    I found a solution that resizes and repositions control to fit a page. That works well, but it also repositions that control on my window.

    here is the code i use for print and scaling :

            //get selected printer capabilities
                System.Printing.PrintCapabilities capabilities = dialog.PrintQueue.GetPrintCapabilities(dialog.PrintTicket);
            //get scale of the print wrt to screen of WPF visual
            double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / mrizka.ActualWidth, capabilities.PageImageableArea.ExtentHeight / mrizka.ActualHeight);
    
            //Transform the Visual to scale
            mrizka.LayoutTransform = new ScaleTransform(scale, scale);
    
            //get the size of the printer page
            Size sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
    
            //update the layout of the visual to the printer page size.
            mrizka.Measure(sz);
            mrizka.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
    
            dialog.PrintVisual(mrizka, mrizka.getID().ToString());
    

    I tried two aproaches to solve this:

    1. Clone my control and then transform cloned one, unaffecting original. Didnt work, for some reason i ended with exception: The provided DependencyObject is not a context for this Freezable, but oddly only in some cases.

    2. Revert size and position changes. I tried calling InvalidateArrange() method, which seemed to work, but only during first call of print method. During second call, it didnt work.

    What should i do please, any ideas< thank you.