WPF FAQ - Printing

Find answers for the most frequently asked questions
Expand All Collapse All

Unfortunately, there is no easy way to print a form. You may implement this function with the steps given below.

1. Add a print function to your application.

To do this, you should add a PrintDocument component to your application. Please drag a PrintDocument from the toolbox to your form. After that, you should create a PrintDialog and add the code to print the document.

[C#]

 private void buttonPrint_Click(object sender, EventArgs e)
        {
            PrintDialog printDialog1 = new PrintDialog();
            printDialog1.Document = printDocument1;
            DialogResult result = printDialog1.ShowDialog();
            if (result == DialogResult.OK)
                printDocument1.Print();
        }

2. Draw the form when printing.

This step is a little complex. You should handle the PrintPage of the printDocument1 and draw the form to the printer device. In the event you may copy the form to an image and then draw it to the printer device.

[C#]

using System.Drawing.Printing;

private void printDocument1_PrintPage( object sender, PrintPageEventArgs e) 
{ 
  Graphics graphic = CreateGraphics(); 
  Image memImage = new Bitmap( Size.Width, Size.Height, graphic ); 
  Graphics memGraphic = Graphics.FromImage( memImage ); 
  IntPtr dc1 = graphic.GetHdc(); 
  IntPtr dc2 = memGraphic.GetHdc(); 
  BitBlt( dc2, 0, 0, ClientRectangle.Width, 
    ClientRectangle.Height, dc1, 0, 0, 13369376 ); 
  graphic.ReleaseHdc( dc1 ); 
  memGraphic.ReleaseHdc( dc2 ); 
  e.Graphics.DrawImage( memImage, 0, 0 ); 
}

3. Declare the API function.

Please note the BitBlt function used in Step 2. It is an unmanaged function. You should use ‘DllImport’ attribute to import it to your code. Although, this is the Step 3, you may perform this step anytime.

[C#]

using System.Runtime.InteropServices;

[ DllImport( 'gdi32.dll' ) ] 
private static extern bool BitBlt( IntPtr hdcDest, 
  int nXDestint nYDest, int nWidthint nHeight,
  IntPtr hdcSrcint nXSrcint nYSrc, System.Int32 dwRop );

Permalink

A BlockUIContainer is a flow content element that allows UIElement to be hosted inside the flow content. This can be done as follows.

[XAML]

<FlowDocument ColumnWidth='400'>
  <Section Background='GhostWhite'>
    <Paragraph>
      A UIElement element may be embedded directly in flow content
      by enclosing it in a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <Button>Click me!</Button>
    </BlockUIContainer>
    <Paragraph>
      The BlockUIContainer element may host no more than one top-level
      UIElement.  However, other UIElements may be nested within the
      UIElement contained by an BlockUIContainer element.  For example,
      a StackPanel can be used to host multiple UIElement elements within
      a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <StackPanel>
        <Label Foreground='Blue'>Choose a value:</Label>
        <ComboBox>
          <ComboBoxItem IsSelected='True'>a</ComboBoxItem>
          <ComboBoxItem>b</ComboBoxItem>
          <ComboBoxItem>c</ComboBoxItem>
        </ComboBox>
        <Label Foreground ='Red'>Choose a value:</Label>
        <StackPanel>
          <RadioButton>x</RadioButton>
          <RadioButton>y</RadioButton>
          <RadioButton>z</RadioButton>
        </StackPanel>
        <Label>Enter a value:</Label>
        <TextBox>
          A text editor embedded in flow content.
        </TextBox>
      </StackPanel>
    </BlockUIContainer>
  </Section>
</FlowDocument>
Permalink

You can use one overload of the AddJob() method to print XML Paper Specification (XPS) files without opening a PrintDialog or in principle, any user interface (UI) at all. You can also print XML Paper Specification (XPS) files using the many Write() and WriteAsync() methods of the XPSDocumentWriter. Another way of printing XML Paper Specification (XPS) is to use the PrintDocument() or PrintVisual() methods of the PrintDialog control.

The main steps to use the three-parameter AddJob(String, String, Boolean) method are as follows.

The example below gives details.
1. Determine if the printer is an XPSDrv printer.

2. The printer is not an XPSDrv printer, set the thread’s apartment to a single thread.
3. Instantiate a print server and print queue object.
4. Call the method specifying a job name, the file to be printed and a Boolean flag indicating whether or not the printer is an XPSDrv printer.

[C#]
class Program
{
    [System.MTAThreadAttribute()] // Added for clarity, but this line is redundant because MTA is the default.
    static void Main(string[] args)
    {
        // Create the secondary thread and pass the printing method for 
        // the constructor’s ThreadStart delegate parameter. The BatchXPSPrinter
        // class is defined below.
        Thread printingThread = new Thread(BatchXPSPrinter.PrintXPS);

        // Set the thread that will use PrintQueue.AddJob to single threading.
        printingThread.SetApartmentState(ApartmentState.STA);

        // Start the printing thread. The method passed to the Thread 
        // constructor will execute.
        printingThread.Start();

    }//end Main

}//end Program class

public class BatchXPSPrinter
{
    public static void PrintXPS()
    {
        // Create print server and print queue.
        LocalPrintServer localPrintServer = new LocalPrintServer();
        PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();

        // Prompt user to identify the directory, and then create the directory object.
        Console.Write('Enter the directory containing the XPS files: ');
        String directoryPath = Console.ReadLine();
        DirectoryInfo dir = new DirectoryInfo(directoryPath);

        // If the user mistyped, end the thread and return to the Main thread.
        if (!dir.Exists)
        {
            Console.WriteLine('There is no such directory.');
        }
        else
        {
            // If there are no XPS files in the directory, end the thread 
            // and return to the Main thread.
            if (dir.GetFiles('*.xps').Length == 0)
            {
                Console.WriteLine('There are no XPS files in the directory.');
            }
            else
            {
                Console.WriteLine('\nJobs will now be added to the print queue.');
                Console.WriteLine('If the queue is not paused and the printer is working, jobs will begin printing.');

                // Batch process all XPS files in the directory.
                foreach (FileInfo f in dir.GetFiles('*.xps'))
                {
                    String nextFile = directoryPath + '\\' + f.Name;
                    Console.WriteLine('Adding {0} to queue.', nextFile);

                    try
                    {
                        // Print the Xps file while providing XPS validation and progress notifications.
                        PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob(f.Name, nextFile, false);
                    }
                    catch (PrintJobException e)
                    {
                        Console.WriteLine('\n\t{0} could not be added to the print queue.', f.Name);
                        if (e.InnerException.Message == 'File contains corrupted data.')
                        {
                            Console.WriteLine('\tIt is not a valid XPS file. Use the isXPS Conformance Tool to debug it.');
                        }
                        Console.WriteLine('\tContinuing with next XPS file.\n');
                    }

                }// end for each XPS file

            }//end if there are no XPS files in the directory

        }//end if the directory does not exist

        Console.WriteLine('Press Enter to end program.');
        Console.ReadLine();

    }// end PrintXPS method

}// end BatchXPSPrinter class

Permalink

The example code begins by refreshing the current print queue object with PrintQueue’s Refresh property. This ensures that the object’s properties accurately represent the state of the physical printer that it represents. Then the application gets the collection of print jobs currently in the print queue by using the “GetPrintJobInfoCollection” method. Next the application loops through the PrintSystemJobInfo collection and compares each ’Submitter’ property with the alias of the complaining user. If they match, the application adds identifying information about the job to the string that will be presented. (The userName and jobList variables are initialized earlier in the application.)

[C#]

foreach (PrintQueue pq in myPrintQueues)
{
    pq.Refresh();
    PrintJobInfoCollection jobs = pq.GetPrintJobInfoCollection();
    foreach (PrintSystemJobInfo job in jobs)
    {
        // Since the user may not be able to articulate which job is problematic,
        // present information about each job the user has submitted.
        if (job.Submitter == userName)
        {
            atLeastOne = true;
            jobList = jobList + '\nServer:' + line;
            jobList = jobList + '\n\tQueue:' + pq.Name;
            jobList = jobList + '\n\tLocation:' + pq.Location;
            jobList = jobList + '\n\t\tJob: ' + job.JobName + ' ID: ' + job.JobIdentifier;
        }
    }// end for each print job    

}// end for each print queue 
Permalink

In the example below, a second print queue is cloned from an existing print queue. The second differs from the first only in its name, location, port, and shared status. The major steps for doing this are as follows.

[C#]

LocalPrintServer myLocalPrintServer = new LocalPrintServer(PrintSystemDesiredAccess.AdministrateServer);
PrintQueue sourcePrintQueue = myLocalPrintServer.DefaultPrintQueue;
PrintPropertyDictionary myPrintProperties = sourcePrintQueue.PropertiesCollection;

// Share the new printer using Remove/Add methods
PrintBooleanProperty shared = new PrintBooleanProperty('IsShared', true);
myPrintProperties.Remove('IsShared');
myPrintProperties.Add('IsShared', shared);

// Give the new printer its share name using SetProperty method
PrintStringProperty theShareName = new PrintStringProperty('ShareName', '\'Son of ' + sourcePrintQueue.Name +'\'');
myPrintProperties.SetProperty('ShareName', theShareName);

// Specify the physical location of the new printer using Remove/Add methods
PrintStringProperty theLocation = new PrintStringProperty('Location', 'the supply room');
myPrintProperties.Remove('Location');
myPrintProperties.Add('Location', theLocation);

// Specify the port for the new printer
String[] port = new String[] { 'COM1:' };


// Install the new printer on the local print server
PrintQueue clonedPrinter = myLocalPrintServer.InstallPrintQueue('My clone of ' + sourcePrintQueue.Name, 'Xerox WCP 35 PS', port, 'WinPrint', myPrintProperties);
myLocalPrintServer.Commit();

// Report outcome
Console.WriteLine('{0} in {1} has been installed and shared as {2}', clonedPrinter.Name, clonedPrinter.Location, clonedPrinter.ShareName);
Console.WriteLine('Press Return to continue ...');
Console.ReadLine(); 
Permalink

Figure and Floater are used to embed content in Flow Documents with placement properties that can be customized independent of the primary content flow. Figure or Floater elements are often used to highlight or accentuate portions of content to host supporting images or other content within the main content flow or to inject loosely related content such as advertisements.

The following example shows how to embed a Figure into a paragraph of text.

[XAML]

<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>

  <Paragraph>
    <Figure 
      Width='300' Height='100' 
      Background='GhostWhite' HorizontalAnchor='PageLeft' >
      <Paragraph FontStyle='Italic' Background='Beige' Foreground='DarkGreen' >
        A Figure embeds content into flow content with placement properties 
        that can be customized independently from the primary content flow
      </Paragraph>
    </Figure>
  </Paragraph>

</FlowDocument>
Permalink

We can define three paragraphs under one section as follows.

In the following example, the section has a “Background” property value of Red, therefore the background color of the paragraphs is also red.

[XAML]

<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <!-- By default, Section applies no formatting to elements contained
       within it. However, in this example, the section has a Background
       property value of 'Red', therefore, the three paragraphs (the block)  
       inside the section also have a red background. -->
  <Section Background='Red'>
    <Paragraph>
      Paragraph 1
    </Paragraph>
    <Paragraph>
      Paragraph 2
    </Paragraph>
    <Paragraph>
      Paragraph 3
    </Paragraph>
  </Section>
</FlowDocument>

Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.