Dear visitor, thanks for stopping by! If you want, you can follow all updates on Snowball.be via RSS. You can also follow me on Twitter or Facebook. More interesting posts from other Microsoft Regional Directors can be found at The Region.
Gill Cleeren     Silverlight     July 30, 2009    

Probably the most requested feature for Silverlight 3 was being able to print or export the contents of a control so that we can save it and then print it. While there is no Print class of some sort directly available in Silverlight 3 either, we can print, using some small workaround. And that workaround goes by the name of the WriteableBitmap.

In this sample, I have created a very simple DTP application (desktop publishing application). It allows the user to draw on a Canvas, add some text and some images. After that, he can print the result. There is room to improve, but it should get you on your way when trying to print from Silverlight 3.

This is the UI of the application.

image

Let’s take a look at how it was done. The XAML code for this sample is quite simple. The Canvas called DrawingCanvas is the one we want to export and print its contents.

            <Grid x:Name="GridApplication">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="20"></RowDefinition>
                    <RowDefinition Height="*"></RowDefinition>
                </Grid.RowDefinitions>
                <Grid x:Name="GridHeader" Background="#47659e" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
                    <TextBlock Foreground="White" Margin="2, 0, 0, 0" FontWeight="Bold">
                    Printing from Silverlight 3
                    </TextBlock>
                </Grid>
                <StackPanel x:Name="ButtonStackPanel" Grid.Row="1" Grid.Column="0" Background="#efedeb">
                    <Rectangle Fill="Black" Height="40" Width="70" Margin="2"></Rectangle>
                    <Button x:Name="ButtonRectangle" Content="Add rectangle" Margin="3" Click="ButtonRectangle_Click" HorizontalAlignment="Center"  ></Button>
                    <Ellipse Fill="Black" Height="40" Width="70" Margin="2"></Ellipse>
                    <Button x:Name="ButtonEllipse" Content="Add Ellipse" Margin="3" Click="ButtonEllipse_Click" HorizontalAlignment="Center"></Button>
                    <Image Source="pictureicon.png" Width="70" Margin="2" Stretch="Uniform"></Image>
                    <Button x:Name="ButtonImage"  Content="Add image" Margin="3"  Click="ButtonImage_Click" HorizontalAlignment="Center"></Button>
                    <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center">
                        <TextBlock Text="T" FontSize="30" FontWeight="Bold" Margin="2" FontFamily="Courier New"  ></TextBlock>
                    </Border>
                    <Button x:Name="ButtonText" Content="Add text" Margin="3" Click="ButtonText_Click"  HorizontalAlignment="Center"></Button>
                    <Rectangle Height="1" Width="130" HorizontalAlignment="Center" Fill="DarkGray" Margin="5"></Rectangle>
                    <Button x:Name="ButtonCreate" Content="Create poster" HorizontalAlignment="Center" Click="ButtonCreate_Click"  Margin="3"></Button>
                    <Button x:Name="ButtonSave" Content="Save poster" HorizontalAlignment="Center" Margin="3" Click="ButtonSave_Click"></Button>
                </StackPanel>
                <Canvas x:Name="DrawingCanvas" Grid.Row="1" Grid.Column="1" Width="648" Height="578" Background="#bbbbbb">

                    
                </Canvas>
            </Grid>

As said, the class that makes printing possible, is the WriteableBitmap. We pass in the control we want to “render” and an arbitrary transform.

WriteableBitmap wb = new WriteableBitmap(DrawingCanvas, null);

Then, we use Joe Stegman’s PNG Encoder. It allows us to create PNG images on the fly. Note that, like mentioned on Joe’s blog, this encoder is not optimal and can be improved both in speed and compression size. We create an instance of the EditableImage class, giving it the size of the control we want to render.

editableImage = new EditableImage((int)DrawingCanvas.ActualWidth, (int)DrawingCanvas.ActualHeight);

At this point, we can start doing a pixel-by-pixel copy of the “canvas-data” to the EditableImage.

Converter cl = new Converter();
for (int idx = 0; idx < editableImage.Height; idx++)      // Height (y)
{
    for (int jdx = 0; jdx < editableImage.Width; jdx++) // Width (x)
    {
        if (wbCounter == wb.Pixels.Length)
            break;
        byte[] colorBytes = cl.ConvIntegertoByteArray(wb.Pixels[wbCounter], 3);
        editableImage.SetPixel(jdx, idx, colorBytes[0], colorBytes[1], colorBytes[2], 255);
        wbCounter++;
    }
}
The image data is now in memory, we can now export and print it using a simple stream and the Silverlight 3 SaveFileDialog.

SaveFileDialog dialog = new SaveFileDialog();
dialog.DefaultExt = "png";

if (dialog.ShowDialog() == true)
{
    using (Stream fs = (Stream)dialog.OpenFile())
    {
        Converter c = new Converter();
        c.CopyStreamContents(editableImage.GetStream(), fs);
        fs.Flush();
    }
}

The result is a generated PNG file, ready to print! The complete sample code can be downloaded here.

kick it on DotNetKicks.com
  Posted on: Thursday, July 30, 2009 9:20:26 PM (Romance Daylight Time, UTC+02:00)   |   Comments [2]
         
9/2/2010   9:32:58 PM