Thursday, 13 September 2012

Print more than one Visual (list of visuals) into a single document in WPF

Print list of visuals in WPF


Visual is the Class in System.Windows.Media

PrinterSettings is the Class in System.Drawing

PrintServer, PrintQueue and EnumeratedPrintQueueTypes are the Classes in System.Printing

XpsDocumentWriter and VisualsToXpsDocument are the Classes in System.Windows.Xps

So, before applying this code, you need to add following assembly references:
-> System.Windows.Media
-> System.Windows.Xps
-> System.Windows.Drawing
-> System.Printing

* This code can also used to fit the visual to the page
* If you select the PDF Xchange for printing, all visuals will get saved into a single pdf file with each visual on each page.

  //Create local variables

  private int m_visualsCount;

  private ObservableCollection<Visual> m_listVisuals = new ObservableCollection<Visual>();

  //Define and initiate PrintDialog which is necessary for printing
  //PrintDialog is in System.Windows.Controls
  PrintDialog printDialog = new PrintDialog();

  //Pass list of visuals through your constructor
  public Constructor(ObservableCollection<Visual> listVisuals)
  {
    InitializeComponent();

    m_visualsCount = listVisuals.Count;  //Calculate number of visuals and assign to local variable

    m_listVisuals = listVisuals; //Assign all visuals in the list to local variable

    //Define and initiate PrinterSettings and get the default printer name
    var printersettings = new PrinterSettings();
    string st = printersettings.PrinterName;

    //Now get all the installed printers on your system into a ComboBox (it can be any other element as well)
    foreach (string str in PrinterSettings.InstalledPrinters)
    {
      combo.Items.Add(str);
    }

    //Default selection of printer in ComboBox
    for (int i = 0; i < PrinterSettings.InstalledPrinters.Count; i++)
    {
      if (PrinterSettings.InstalledPrinters[i].Equals(st))
      {
        combo.SelectedIndex = i;
      }
    }

    //Now initiate the PrintServer and get Print Queues
    var server = new PrintServer();
    var queues = server.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections});

    //Set the status of selected printer(just to display on GUI)
    foreach (var queue in queues)
    {
     if(st.Equals(queue.FullName))
     {
       sp.Text(any textbox in Xaml code) = queue.QueueStatus.ToString();
     }
    }

    //To show the number of pages to be printed
    pagesCount.Text(any textbox in Xaml code) = m_visualsCount.ToString();
  }

  //When the printing is initiated
  private void buttonDone_Click(object sender, RoutedEventArgs e)
  {
    //Get the printer, server and queue that are currently selected
    string str = PrinterSettings.InstalledPrinters[combo.SelectedIndex];
    var server = new PrintServer();
    var queues = server.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections });

    Find the selected printer for printing
    foreach (var queue in queues)
    {
      if (str.Equals(queue.FullName))
      {
        // Get an XpsDocumentWriter for the default print queue
        XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(queue);

        VisualsToXpsDocument vtxd = (VisualsToXpsDocument)xpsdw.CreateVisualsCollator();

        //Begin the batchwrite
        vtxd.BeginBatchWrite();

        //Scale and Write all visuals
        for (int count = 0; count < m_visualsCount; count++)
          {
            if (m_listVisuals[count] != null)
            {
              //Scale the visual
              Visual scaledVisual = ScaleVisual(m_listVisuals[count], queue);

              //get selected printer capabilities
              PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);

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

              //Transform the Visual to scale
              this.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.
              this.Measure(sz);
              this.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));

              //Now write the visual to printer to fit on the one page.
              vtxd.Write(scaledVisual);
            }
          }

          //Mark the end of the batch operation.
          vtxd.EndBatchWrite();

          this.Close();
        }
      }
    }
  }

  //Setting the properties of Selected Printer
  private void combo_SelectionChanged(object sender, SelectionChangedEventArgs e)
  {
    string st = combo.SelectedValue.ToString();

    var server = new PrintServer();
    var queues = server.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections });

    foreach (var queue in queues)
    {
      if (st.Equals(queue.FullName))
      {
        sp.Text = queue.QueueStatus.ToString();

        printDialog.PrintQueue = queue;
          printDialog.PrintTicket = queue.UserPrintTicket;
        }
      }
    }
  }

  //We should able to scale the visual so it fits within the page.
  private Visual ScaleVisual(Visual visual, PrintQueue printQueue)
  {
    ContainerVisual root = new ContainerVisual();

    //An inch is 96 DIPs, use this to scale up sizes given in inches.
    double inch = 96;

    //Calculate our margins.
    double xMargin = 1.25 * inch;
    double yMargin = 1 * inch;

    //Get the current print ticket from which the paper size can be obtained.
    PrintTicket printTicket = printQueue.UserPrintTicket;

    //Retrieve the dimensions of the target page.
    double pageWidth = printTicket.PageMediaSize.Width.Value;
    double pageHeight = printTicket.PageMediaSize.Height.Value;
    double xScale = (pageWidth - xMargin * 2) / pageWidth;
    double yScale = (pageHeight - yMargin * 2) / pageHeight;

    //Add the visual to ContainerVisual(root)
    root.Children.Add(visual);
    root.Transform = new MatrixTransform(xScale, 0, 0, yScale, xMargin, yMargin);

    return root;
  }

2 comments:

  1. Thanks to the writer of this article. I appreciate your effort in making this informational blogs. I know it's not easy to do this but you have done a really great job. Congrats. I'm pretty sure your readers enjoying it a lots.


    Rica
    www.imarksweb.org

    ReplyDelete
  2. Thanks for sharing such a wonderful article, I hope you could inspire more people. Visit my site too.

    n8fan.net

    www.n8fan.net

    ReplyDelete