Tuesday, June 28, 2011

Silverlight 4 Printing Centered

In one of my previous article, I've explained about printing fit to page in Silverlight 4(Silverlight4 - Printing fit to page). In this article, I'll enhance the PrintUtility(specified in the Snippet 2 of post) to print the element centered in the print area(center of the paper).

Let us recap the idea of printing fit to page. 
  1. The print element is transformed (scaled) to fit to page, when the print element size exceeds the print area - Scaled.
  2. The print element is printed as it is otherwise - Normal.
During scale transformation, the print element is scaled in such a way to maintain the aspect ratio. So it is clear that we need to handle both the scenarios (Scaled & Normal) while centering the print element.

Ok, now we need to identify how much the print element need to be positioned left and top. A simple calculation will do that for us.

Left position = ((total print area width) - (print element width)) / 2, Top position = ((total print area height) - (print element height)) / 2.

Initially I thought of setting the margin to the print element to achieve centering. But after that I got an alternate way from an article. As most of you know three primary transformations are Transformation, Rotation and Scaling. It is the translate transformation that we're going to use for centering.

Change will be there only in the DocToPrint_PrintPage handler with respective calculations and transformation included. Below is the complete code snippet of the PrintUtility.

Snippet 1

public class PrintUtility
{
    #region Class Level Variables

    private const string NullReferenceExceptionMessage = "PrintElement is null : Please specify the element to be printed.";

    private Grid parentGrid;

    private Border borderItem;

    private BusyIndicatorControl busyIndicatorControl;

    private WriteableBitmap printElementImage;

    private ImageBrush printElementImageBrush;

    #endregion

    #region Events

    #endregion

    #region Properties

    public FrameworkElement PrintElement { get; private set; }

    public string DocumentName { get; private set; }

    public FrameworkElement TabletUIElement { get; set; }

    #endregion

    #region Constructors

    public PrintUtility(FrameworkElement elementToBePrinted, string documentName)
    {
        this.PrintElement = elementToBePrinted;
        this.DocumentName = documentName;
    }

    #endregion

    #region Event Handlers

    private void DocToPrint_EndPrint(object sender, EndPrintEventArgs e)
    {
        this.PrintElement.RenderTransform = null;

        if (this.borderItem != null)
        {
            if (this.parentGrid.Children.Contains(this.borderItem))
            {
                this.parentGrid.Children.Remove(this.borderItem);
            }

            this.borderItem = null;
        }

        this.parentGrid = null;
        this.printElementImage = null;
        this.printElementImageBrush = null;
    }

    private void DocToPrint_BeginPrint(object sender, BeginPrintEventArgs e)
    {
        FrameworkElement maskElement = this.TabletUIElement ?? this.PrintElement;

        if (maskElement.Parent is Grid)
        {
            this.parentGrid = (Grid)maskElement.Parent;
            this.parentGrid.Children.Add(this.BuildBorderWithImageFill());
        }
    }

    private void DocToPrint_PrintPage(object sender, PrintPageEventArgs e)
    {
        double scale = 1;
        TransformGroup transformGroup = new TransformGroup();

        if (e.PrintableArea.Height < this.PrintElement.ActualHeight)
        {
            scale = e.PrintableArea.Height / this.PrintElement.ActualHeight;
        }

        if (e.PrintableArea.Width < this.PrintElement.ActualWidth && e.PrintableArea.Width / this.PrintElement.ActualWidth < scale)
        {
            scale = e.PrintableArea.Width / this.PrintElement.ActualWidth;
        }

        if (scale < 1)
        {
            ScaleTransform scaleTransform = new ScaleTransform();
            scaleTransform.ScaleX = scale;
            scaleTransform.ScaleY = scale;

            TranslateTransform translateTransform = new TranslateTransform();
            translateTransform.X = (e.PrintableArea.Width - (this.PrintElement.ActualWidth * scale)) / 2;
            translateTransform.Y = (e.PrintableArea.Height - (this.PrintElement.ActualHeight * scale)) / 2;

            transformGroup.Children.Add(scaleTransform);
            transformGroup.Children.Add(translateTransform);
        }
        else
        {
            TranslateTransform translateTransform = new TranslateTransform();
            translateTransform.X = (e.PrintableArea.Width - this.PrintElement.ActualWidth) / 2;
            translateTransform.Y = (e.PrintableArea.Height - this.PrintElement.ActualHeight) / 2;

            transformGroup.Children.Add(translateTransform);
        }

        this.PrintElement.RenderTransform = transformGroup;
        e.PageVisual = this.PrintElement;
    }

    #endregion

    #region Public Methods

    public void Print()
    {
        if (this.PrintElement != null)
        {
            PrintDocument docToPrint = new PrintDocument();

            docToPrint.PrintPage += new EventHandler<PrintPageEventArgs>(this.DocToPrint_PrintPage);
            docToPrint.BeginPrint += new EventHandler<BeginPrintEventArgs>(this.DocToPrint_BeginPrint);
            docToPrint.EndPrint += new EventHandler<EndPrintEventArgs>(this.DocToPrint_EndPrint);

            docToPrint.Print(this.DocumentName);
        }
        else
        {
            throw new NullReferenceException(NullReferenceExceptionMessage);
        }
    }

    #endregion

    #region Private Methods

    private BusyIndicatorControl BuildBusyIndicatorControl()
    {
        this.busyIndicatorControl = new BusyIndicatorControl();

        this.busyIndicatorControl.IsBusy = true;
        this.busyIndicatorControl.BusyContent = "Processing For Printing......";

        return this.busyIndicatorControl;
    }

    private Border BuildBorderWithImageFill()
    {
        this.printElementImageBrush = new ImageBrush();
        this.borderItem = new Border();

        this.printElementImage = new WriteableBitmap(this.TabletUIElement ?? this.PrintElement, null);
        this.printElementImageBrush.ImageSource = this.printElementImage;

        this.borderItem.Background = this.printElementImageBrush;
        this.borderItem.Child = this.BuildBusyIndicatorControl();

        return this.borderItem;
    }

    #endregion
}


No comments:

Post a Comment

Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.