Printing. Ugh. Every programmer I know hates writing code to do
printing. Stuff never seems to appear quite how it should - the
transition from the screen to the page can often be a very messy one.
Printing with the Win32 API is ugly - something more akin to black magic
and dark incantations than actual computer code. The .NET framework made
it a little bit better in WinForms, but it was still just a very thin
wrapper around the Win32 api - and it was still in the world of GDI. But
now we are in the land of WPF! The land of lollipops and hope and
magical wonders! So everything should be awesome, right?
Well, actually, in many ways it is. It is a lot easier to just step
right up and do some printing - and in all likelihood what you see on
the screen will match what comes out on paper. This is due to two major
factors. The obvious one is that they made a nicer API - it is a much
thicker wrapper around the Win32 black magic than what WinForms had. The
more subtle factor is the fact that WPF is all vector based graphics and
does not deal directly with pixels. This means that many more things
scale exactly as you expect when you go from the 96 DPI world of the
computer monitor to the 300-1200 DPI world of the printer.
OK, enough abstract talk. Let's do some printing. Below you can see a
screenshot of a simple sample app. When the "Print Me" button is
pressed, the canvas area above the button is printed.
And here is an example of what that printed output looks like (in this
case, printed through the Windows XPS printer, so that I could take a
screenshot):
And it looks like that if you print it out as well (assuming you print
it in color :P). So what is the code to do this? Well, we of course
defined that interface in a couple lines of XAML:
<Window x:Class="WpfPrinting.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="360" Width="325">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Canvas Grid.Row="0" x:Name="_PrintCanvas" Margin="10">
<TextBlock Canvas.Left="30" Canvas.Top="70">
A bunch of text.
</TextBlock>
<Ellipse Width="30" Height="50" Canvas.Left="200"
Canvas.Top="10" Fill="Blue" />
<Rectangle Width="100" Height="10" Canvas.Left="175"
Canvas.Top="150" Fill="Red" />
<TextBlock FontSize="100" Foreground="Green"
FontWeight="Bold" Canvas.Top="150"
Canvas.Left="20">
{ }
</TextBlock>
</Canvas>
<Button Click="PrintClick" Grid.Row="1">
Print Me
</Button>
</Grid>
</Window>
So now we have that canvas area defined in XAML. How do we print it?
Well, that is the great thing about WPF printing - you can print any
Visual! This canvas is a visual, so we can just straight up print it:
private void PrintClick(object sender, RoutedEventArgs e)
{
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{ dialog.PrintVisual(_PrintCanvas, "My Canvas"); }
}
That is actually all you need to do to print a visual. Well, actually,
you don't even need to show the dialog - it would just print to the
default printer, then. All we are doing here is creating a
PrintDialog,
showing it, and (if the user clicks ok) calling
PrintVisual
(which takes a visual and a name).
Calling
PrintVisual
will always print what you give it to a single
page, and it will be placed in the very upper left of the page. This
means that if your visual is larger than a page, it will get clipped,
and if your visual has no built in margin, it will probably clip a
little bit on the edges (because most printers can't print on the very
edges of pages).
For doing more complex, multiple page printing, there is another method
called
PrintDocument,
which takes a
DocumentPaginator.
However, that is a topic for another tutorial.
OK, so we have seen how to print out a visual that already exists - but
how do we print out something we have created on the fly? Well, all it
does is take a couple extra lines:
private void PrintSomethingNew()
{
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() != true)
{ return; }
StackPanel myPanel = new StackPanel();
myPanel.Margin = new Thickness(15);
Image myImage = new Image();
myImage.Width = 128;
myImage.Stretch = Stretch.Uniform;
myImage.Source = new BitmapImage(new Uri("C:\\Tree.jpg", UriKind.Absolute));
myPanel.Children.Add(myImage);
TextBlock myBlock = new TextBlock();
myBlock.Text = "A Great Image.";
myBlock.TextAlignment = TextAlignment.Center;
myPanel.Children.Add(myBlock);
myPanel.Measure(new Size(dialog.PrintableAreaWidth,
dialog.PrintableAreaHeight));
myPanel.Arrange(new Rect(new Point(0, 0),
myPanel.DesiredSize));
dialog.PrintVisual(myPanel, "A Great Image.");
}
This code prints out something that looks like this:
As you might expect, we have to do a bunch of element creation so we can
get something laid out. The interesting pieces are the call to
Measure
and Arrange
. Since these elements have never appeared on the screen,
they have never been rendered. But if we don't render them, then the
printout will just be blank. So we have to do our own calls to Measure
and Arrange
. In this case, we do a Measure
using the page size of
the printer we are printing to. This page size is available off of the
PrintDialog
, using the PrintableAreaWidth
and PrintableAreaHeight
properties. And once measuring is done, we arrange (using the desired
size produced through the measure pass).
You might think that those two properties represent the printable area
on a page, but it is actually just the exact size of the page in DIU (so
for an 8.5x11 piece of paper, the width and height are 816 and 1056,
respectively). This means that you still need to account for the fact
that most printers can't print on the extreme edge of a page, which is
why I set a margin on
myPanel
.
Well, that is it for the basics of printing in WPF. You can grab the the
Visual Studio project for this code below. And stay tuned for more
printing tutorials - delving into writing your own DocumentPaginator, as
well as using the PrintQueue and the XpsDocumentWriter.
Source Files:
Mudra Exchange is India's foremost secure crypto exchange platform, including SSL, TSL, and other cybersecurity elements.
ReplyDeletefor more information visit our site www.mudra.exchange
There a lot of ways to get a message to your audience. Choosing the wrong one could lead to very bad results. A basic knowledge of what is available will help you avoid the pitfalls that have trapped so many others. In this article, we will look at three of the types of printing we might use in an effective marketing campaign. Specifically, offset printing, digital printing and hybrid printing. asia printing
ReplyDelete