Making printing easier in Silverlight 4


Well, what an exciting week!  First Visual Studio 2010 is released, followed by Silverlight 4 yesterday!  Consequently, I was inspired to post about something new!  I’ve been spending some time looking at the new printing feature in Silverlight 4, and while on the surface it looks like a pretty simple and lower-level set of APIs, it’s possible to build rich frameworks on top of them for accomplishing common printing tasks.  In this post, I’ll take a look at an attempt I made (and added to SLaB) at building such a higher-level API over printing that makes printing collections of data easier.

Specifically, I’ve been building a CollectionPrinter control – a control that paginates, previews, and prints collections in a template-driven, designer-friendly way.  With this control, printing a collection can be entirely XAML-based and code-free!

For example, the image below shows some sample data (courtesy of the Blend 4 RC) within a DataGrid being printed across multiple pages.

A printed collection.

And you can see it in action by clicking here.

Of course, this is just an example of how one might want to build such a library, but it hopefully inspires some ideas you all might have around printing!  I’d love to know what you think!

So, how does printing work?

The printing APIs in Silverlight 4 center around the PrintDocument class.  SilverlightShow has a great article on the basics of using this class.  Basically, the workflow for printing in Silverlight 4 is the following:

  1. Create a new PrintDocument
  2. Attach a handler to the PrintDocument.PrintPage event, which will be called for each page you choose to print
  3. Call PrintDocument.Print(), passing in a document name (which will appear in the print spooler, for example)
  4. The user is prompted to print
  5. In each PrintPage call:
    1. If you have content ready to print:
      1. Choose some UI to print to the page and set the PageVisual in the PrintPageEventArgs
      2. If there are more pages to print, set HasMorePages on PrintPageEventPargs to true
    2. If you’re not ready to print yet:
      1. Set PageVisual to null
      2. Set HasMorePages to true (you will be called back after a short delay for the content – up to 8 attempts will be made to print a page before printing fails)

And that’s it!  For each page, Silverlight will render a bitmap of the visuals you provided in PageVisual (just like with WriteableBitmap), then send this bitmap to the printer.

That all seems pretty straightforward… why do I need anything more?

Well, it is pretty straightforward.  However, there’s a fair amount of work that you would have to do in order to create a coherently printed document:

  • Pagination – in order to figure out which items to print on each page, you will need to add items to the page, searching for the item that would flow beyond the end of the page.  This involves measuring/arranging pages and expanding templates, keeping track of the items you’ve printed so far, handling cases where items are too big for the page, and so on.
  • Page layout – it’s very common to want headers/footers on your pages, which further complicates the pagination process.
  • Page context – when printing a particular page, you often want some additional context, such as which items are on the page, the page number, the total number of pages (so that you can print “Page 1 / 10”, whether this is the first or last page, etc.  This context must be calculated and tracked, and is difficult when the printing process is progressive as with the above API (e.g. how can I print the first page without first having rendered all of the pages, so that I know what the total page count will be?)
  • Dealing with content not in the visual tree – often, you want to print UI directly from the visual tree, which is much easier.  But when printing controls that are NOT in the visual tree, life gets much more complicated.  As with WriteableBitmap, controls used as the PageVisual don’t get a full visual tree pass, meaning that events like FrameworkElement.Loaded don’t get raised.  Some controls use such events to do initialization, such as the charting controls in the Silverlight Toolkit.  In order to properly print arbitrary controls, it’s often helpful to actually place the control in the visual tree.
  • Handling animations – many controls (again, such as the charting controls in the Silverlight Toolkit) have animations and transitions by default.  For the charting controls, the data points being charted fade in by default.  If you try to print this when the control is created, you’ll get a blank chart!  One way to deal with this (if it’s the desired behavior) is to walk the visual tree, causing any running storyboard to SkipToFill.
  • Print preview – while it’s not really possible to give a full preview of what a printed document will look like (because you can’t find out what the size/margins of the page will be before printing has started), one can approximate it and at least provide a hint as to what to expect when printing begins.
  • Designability – Working with pages to print in Blend or Visual Studio’s designer can be somewhat difficult, especially if you need to write the code to handle the above cases.

As you can see, there are a number of complicating factors when doing sophisticated printing, but these can be abstracted away if the domain you’re given is specific enough.  In this case, we’re printing a collection, and we can use the items in the collection as the units between which we can break up pages.

OK, cool, I think I get it.  So, how does it work?

You can use the CollectionPrinter much like an ItemsControl.  It’s fundamentally DataTemplate-driven.  It has an ItemsSource and an ItemTemplate, but instead, you can specify a BodyTemplate (which, by default, is an ItemsControl :) ).  For headers, footers, and such, you can specify additional DataTemplates.

All of the DataTemplates are bound to a “CollectionPrintContext” which contains the following pieces of information:

  • CurrentItems – the set of items being printed on this page (this collection will be built up dynamically during printing/previewing)
  • CurrentPage/CurrentPageIndex – the page number (1- or 0-based) being printed
  • First/Last items (so that you can print “Items 1-10” or “Aa – Aardvark” on each page):
    • FirstItem/FirstItemIndex/FirstItemValue – the first item being printed on the page (1-based index, 0-based index, or actual value, respectively)
    • LastItem/LastItemIndex/LastItemValue – the last item being printed on the page (1-based index, 0-based index, or actual value, respectively)
  • PageCount – the total number of pages to print (note: this is a nullable int value and may not be provided if there isn’t time to finish pre-rendering all pages before the PrintPage callback limit is reached)
  • PageMargins – the margins of the page being printed
  • PrintableArea – the size of the space in which the page can be printed

The hope is that with these pieces of information to bind to, you can print rich pages for your users.  The rest is just creative use of bindings :)

To kick off printing, either call the Print() method on the CollectionPrinter or bind a button to the PrintCommand property on the CollectionPrinter for completely code-free printing!

The CollectionPrinter will attempt to address all of the issues above, spawning invisible popups to ensure that the UI actually has a moment to be in the visual tree, walking the visual tree to skip animations to fill, and most importantly paginating all of the items in the ItemsSource.

Let’s see it in action!

Using an ItemTemplate

The most basic case for printing with the CollectionPrinter is to use an ItemTemplate, just as you would with an ItemsControl.  The CollectionPrinter will happily handle cases where items are irregularly sized, so be as rich as you’d like!

For example:

ItemTemplate-based Printing

The XAML style for this CollectionPrinter is as follows:

<Style x:Key="PrintStyle"
        TargetType="SLaB:CollectionPrinter">
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <Border BorderThickness="1"
                        BorderBrush="Gray">
                    <Grid>
                        <Grid.Resources>
                            <Style TargetType="TextBlock">
                                <Setter Property="FontSize"
                                        Value="12" />
                            </Style>
                        </Grid.Resources>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <StackPanel Grid.Column="0"
                                    Margin="10">
                            <Image Source="{Binding Photo}"
                                   Height="{Binding Age}"
                                   Width="{Binding Age}"
                                   HorizontalAlignment="Left" />
                            <TextBlock Text="{Binding Name, StringFormat='Name: {0}'}" />
                            <TextBlock Text="{Binding Age, StringFormat='Age: {0}'}" />
                            <TextBlock Text="{Binding Address, StringFormat='Address: {0}'}"
                                       TextWrapping="Wrap" />
                        </StackPanel>
                        <toolkit:Chart Grid.Column="1"
                                       Title="{Binding Name}">
                            <toolkit:ScatterSeries DependentValuePath="X"
                                                   IndependentValuePath="Y"
                                                   Title="Values"
                                                   ItemsSource="{Binding Values}" />
                        </toolkit:Chart>
                    </Grid>
                </Border>
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate> ... </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="FooterTemplate">
        <Setter.Value>
            <DataTemplate> ... </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
<SLaB:CollectionPrinter x:Name="printer"
                        Style="{StaticResource PrintStyle}" />

All it takes to print based on this style is setting the ItemsSource (in this case to some Blend sample data) and binding a button to the PrintCommand on the control.  In my header and footer templates, I have controls that are bound to the CollectionPrintContext, and whose visibilities are determined by binding to things like IsFirstPage (see the source included in the SLaB download for more details).

Changing the BodyTemplate (and printing a DataGrid)

Now, that’s all wonderful if all I want to do is print something that looks like an ItemsControl.  But what can we do if we want to print a DataGrid (with column headers, etc.)?  Well, the CollectionPrinter doesn’t depend on being given an ItemsControl at any point.  Instead, it determines pagination based upon the DesiredSize of its content after measuring/arranging.  As a result, anything that grows as its bound content is changed will work with the CollectionPrinter.  You can easily print a DataGrid in this way, as I’ve done below:

DataGrid-based PrintingAnd the XAML barely changes from what you see above.  In this case, instead of using an ItemTemplate, I set a BodyTemplate, which is a DataGrid (with vertical scrolling disabled) with a variety of columns.  In this case, I’ll specify the DataTemplates inline:

<SLaB:CollectionPrinter xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
                        xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:SLaB="http://www.davidpoll.com/SLaB">
    <SLaB:CollectionPrinter.HeaderTemplate>
        <DataTemplate> ... </DataTemplate>
    </SLaB:CollectionPrinter.HeaderTemplate>
    <SLaB:CollectionPrinter.FooterTemplate>
        <DataTemplate> ... </DataTemplate>
    </SLaB:CollectionPrinter.FooterTemplate>
    <SLaB:CollectionPrinter.BodyTemplate>
        <DataTemplate>
            <sdk:DataGrid ItemsSource="{Binding CurrentItems}"
                          AutoGenerateColumns="False"
                          VerticalScrollBarVisibility="Disabled">
                <sdk:DataGrid.Columns>
                    <sdk:DataGridTextColumn Binding="{Binding Name}"
                                            Header="Name" />
                    <sdk:DataGridTextColumn Binding="{Binding Address}"
                                            Header="Address" />
                    <sdk:DataGridTextColumn Binding="{Binding Age}"
                                            Header="Age" />
                    <sdk:DataGridTemplateColumn Header="Image"
                                                IsReadOnly="True">
                        <sdk:DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Photo}"
                                       Height="50"
                                       Width="50" />
                            </DataTemplate>
                        </sdk:DataGridTemplateColumn.CellTemplate>
                    </sdk:DataGridTemplateColumn>
                    <sdk:DataGridTemplateColumn Header="Values"
                                                Width="*"
                                                IsReadOnly="True">
                        <sdk:DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <chartingToolkit:Chart Title="{Binding Name}">
                                    <chartingToolkit:ScatterSeries DependentValuePath="X"
                                                                   IndependentValuePath="Y"
                                                                   Title="Values"
                                                                   ItemsSource="{Binding Values}" />
                                </chartingToolkit:Chart>
                            </DataTemplate>
                        </sdk:DataGridTemplateColumn.CellTemplate>
                    </sdk:DataGridTemplateColumn>
                </sdk:DataGrid.Columns>
            </sdk:DataGrid>
        </DataTemplate>
    </SLaB:CollectionPrinter.BodyTemplate>
</SLaB:CollectionPrinter>

Explicitly providing individual pages

You can also use the CollectionPrinter for printing multiple pages explicitly (automatically dealing with all of the PrintPage callbacks, running animations, etc.) by providing a series of DataTemplates (one for each page) as the ItemsSource, changing the BodyTemplate to a ContentControl, and setting the maximum number of items to print per page to 1.  For example:

Individual Pages

And, of course, the corresponding XAML:

<SLaB:ObservableObjectCollection x:Key="Pages">
    <DataTemplate>
        <StackPanel>
            <TextBlock FontWeight="Bold"
                        FontSize="20">Lorem Ipsum</TextBlock>
            <TextBlock TextWrapping="Wrap"
                        Margin="10"
                        xml:space="preserve">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ultrices cursus tortor ac egestas. Pellentesque semper lobortis enim, vel imperdiet dolor vehicula ac. Suspendisse auctor tempus molestie. Cras pulvinar sagittis libero, vel pretium ipsum consectetur sit amet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum feugiat nunc eget ante euismod ac facilisis velit aliquet. Vestibulum eget nulla magna, eget scelerisque ligula. Cras nec nisi faucibus leo fermentum euismod eu vel lacus. Etiam lacus massa, pulvinar id tempor eget, varius at lorem. Praesent venenatis nisi ac ipsum facilisis at suscipit magna sollicitudin. Phasellus placerat imperdiet hendrerit. Nulla ac risus velit. Sed orci lorem, imperdiet vel ultrices et, viverra ut leo. Mauris feugiat, diam eget mollis tempus, est leo pellentesque risus, vitae lacinia ante felis hendrerit elit. 

Suspendisse potenti. Donec dui justo, ultrices quis condimentum vel, bibendum vel nisi. Pellentesque suscipit fermentum dui vel sodales. Nulla vitae tortor vel orci posuere vestibulum. Curabitur non lacus quam. Nulla sit amet tempor libero. Integer dictum lectus ut sem adipiscing vitae fringilla felis accumsan. Mauris ut risus felis, ut pulvinar quam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Integer fermentum, turpis sit amet tincidunt fermentum, metus mauris bibendum nisi, nec tincidunt purus enim nec nisi. Sed faucibus congue ultricies. Maecenas sed lorem id sem ornare imperdiet ut vitae libero. Curabitur mi diam, ornare sit amet dignissim eu, imperdiet sed nibh. Donec ultrices libero sed ipsum sollicitudin in dapibus elit rutrum. Nulla egestas tempus est, nec semper lacus sodales vel. Quisque consectetur turpis nunc, eu pretium felis. Etiam non adipiscing elit. 

Cras sit amet volutpat metus. Nunc eu augue eu urna placerat adipiscing in vel lacus. Etiam auctor orci nec dui adipiscing non viverra nisl gravida. In lacinia venenatis lobortis. Vestibulum dignissim, dolor ut feugiat ultricies, eros odio adipiscing augue, quis congue turpis augue quis mauris. Aliquam at ligula sem. Aenean eget arcu ac odio eleifend convallis. Aenean eu tellus ac eros placerat aliquam. Nam consectetur neque sed massa accumsan mollis. Pellentesque in mi erat, eget tristique elit. Praesent mattis magna sed est placerat bibendum venenatis nulla facilisis. Duis nec mollis nisi. Vestibulum et eros vitae felis vestibulum scelerisque. Donec venenatis, nulla vel rutrum tempus, purus nulla feugiat felis, eu semper diam nibh tincidunt metus. 

Praesent venenatis aliquet vulputate. In suscipit, nulla ut pulvinar ullamcorper, diam ligula sagittis enim, fermentum tempus nunc neque sed nisi. Vivamus aliquam rutrum scelerisque. Phasellus suscipit, quam sed suscipit pretium, massa nunc elementum lectus, et adipiscing arcu turpis sed dolor. Suspendisse potenti. Proin nisl mauris, sodales tincidunt ultricies sed, placerat quis enim. Nulla elementum nunc vel sapien venenatis venenatis vel eu ligula. Sed vitae erat ante. Etiam nec sapien nec sapien sagittis hendrerit. Duis at odio dolor. Sed condimentum euismod felis, ut congue dolor luctus quis. Duis non tellus enim. Quisque quis odio erat. Nulla nulla mi, dapibus ut euismod ut, adipiscing nec lacus. Aliquam faucibus dui at est accumsan ut laoreet erat pellentesque. Nullam id malesuada tellus. Donec et ligula tincidunt metus rutrum pretium. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; 

Sed lacinia dignissim scelerisque. Duis pharetra elit et nisl euismod viverra. Nam semper, purus ut luctus tincidunt, eros nisi aliquam nunc, non semper lorem enim sit amet augue. Vestibulum adipiscing tortor a magna tristique fringilla. Etiam porta volutpat odio, eu posuere velit mollis non. Mauris ut arcu quis lectus dapibus condimentum. Pellentesque non bibendum nisi. In hac habitasse platea dictumst. Maecenas laoreet lorem ut sem pellentesque id facilisis nibh pulvinar. Vivamus tempus erat placerat diam condimentum ac bibendum felis egestas. Quisque mollis hendrerit risus, ac euismod metus dapibus nec. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus quis ipsum euismod dolor pharetra viverra eget a augue. Morbi lorem enim, porta ut congue quis, pretium et enim.
            </TextBlock>
        </StackPanel>
    </DataTemplate>
    <DataTemplate>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBlock FontWeight="Bold"
                        FontSize="20">Lorem Ipsum</TextBlock>
            <Image Grid.Row="1"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Source="/ScratchPrintingProject;component/SLaB Logo.png" />
        </Grid>
    </DataTemplate>
</SLaB:ObservableObjectCollection>
<Style x:Key="PrintStyle"
        TargetType="SLaB:CollectionPrinter">
    <Setter Property="MaximumItemsPerPage"
            Value="1" />
    <Setter Property="BodyTemplate">
        <Setter.Value>
            <DataTemplate>
                <ContentControl ContentTemplate="{Binding CurrentItems[0]}"
                                HorizontalContentAlignment="Stretch"
                                VerticalContentAlignment="Stretch" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate> ... </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
<SLaB:CollectionPrinter x:Name="printer"
                        ItemsSource="{StaticResource Pages}"
                        Style="{StaticResource PrintStyle}" />

Above, I have two pages: one with a bunch of text, and one with a large image on it.  I still get all of the context for my headers/footers, and can use this as a way to get the benefits of using the CollectionPrinter without being locked into an ItemsControl-like behavior.

Headers and Footers

Creating Headers and Footers for each page is also really easy with the CollectionPrinter – just specify a HeaderTemplate or a FooterTemplate.  These DataTemplates are bound to the CollectionPrintContext, which you can use to generate your header and footer info.  I use some ValueConverters (in one of the SLaB libraries, if you’d like to reuse them) to conditionalize Visibility of a title for a document to the first page, and to change colors/text for the other pages.  I also use the FirstItemValue/LastItemValue to provide a “Aa – Aardvark” (like you’d find in a dictionary) footer on each page.

For example, the sample pages above use the following XAML for their header/footers:

<SLaB:CollectionPrinter.HeaderTemplate>
    <DataTemplate>
        <StackPanel HorizontalAlignment="Stretch">
            <StackPanel.Resources>
                <SLaB:BoolConverter x:Key="BoolConverter" />
            </StackPanel.Resources>
            <StackPanel HorizontalAlignment="Right"
                        Orientation="Horizontal">
                <TextBlock Text="{Binding CurrentPage, StringFormat='{}Page {0} '}" />
                <TextBlock Text="{Binding PageCount, StringFormat='{}/ {0}'}" />
            </StackPanel>
            <TextBlock HorizontalAlignment="Center"
                        Visibility="{Binding IsFirstPage, Converter={StaticResource BoolConverter}}"
                        FontSize="32">This is a test document!</TextBlock>
            <TextBlock HorizontalAlignment="Center"
                        Visibility="{Binding IsLastPage, Converter={StaticResource BoolConverter}}"
                        FontSize="16">This is the last page!</TextBlock>
            <TextBlock HorizontalAlignment="Center"
                        FontSize="14">
                <TextBlock.Foreground>
                    <Binding Path="CurrentPage">
                        <Binding.Converter>
                            <SLaB:EvenOddConverter>
                                <SLaB:EvenOddConverter.Even>
                                    <SolidColorBrush Color="Blue" />
                                </SLaB:EvenOddConverter.Even>
                                <SLaB:EvenOddConverter.Odd>
                                    <SolidColorBrush Color="Red" />
                                </SLaB:EvenOddConverter.Odd>
                            </SLaB:EvenOddConverter>
                        </Binding.Converter>
                    </Binding>
                </TextBlock.Foreground>
                <TextBlock.Text>
                    <Binding Path="CurrentPage"
                                StringFormat="This page is {0}">
                        <Binding.Converter>
                            <SLaB:EvenOddConverter>
                                <SLaB:EvenOddConverter.Even>
                                    Even
                                </SLaB:EvenOddConverter.Even>
                                <SLaB:EvenOddConverter.Odd>
                                    Odd
                                </SLaB:EvenOddConverter.Odd>
                            </SLaB:EvenOddConverter>
                        </Binding.Converter>
                    </Binding>
                </TextBlock.Text>
            </TextBlock>
        </StackPanel>
    </DataTemplate>
</SLaB:CollectionPrinter.HeaderTemplate>
<SLaB:CollectionPrinter.FooterTemplate>
    <DataTemplate>
        <StackPanel HorizontalAlignment="Center"
                    Orientation="Horizontal">
            <TextBlock Text="{Binding FirstItemValue.Name}" />
            <TextBlock Text=" - " />
            <TextBlock Text="{Binding LastItemValue.Name}" />
        </StackPanel>
    </DataTemplate>
</SLaB:CollectionPrinter.FooterTemplate>

Nifty, isn’t it? :)

That’s a lot of XAML!  Do I have to write all of that by hand?

Of course not!  In fact, with the CollectionPrinter, Blend gives you a nice little print preview (especially if you use it as the root of a XAML file) and lets you modify the various templates directly in Blend, inline with the rest of the page!  You can adjust the “CurrentPageIndex” property to preview your CollectionPrinter configuration in the designer.

Editing templates within Blend

To accomplish this in Blend:

  • Select a CollectionPrinter on the designer (either making it the root of a XAML document or selecting it in your existing XAML)
  • Right-click the CollectionPrinter in the Objects and Timeline Window
  • Choose “Edit Additional Templates”
  • Choose a template to edit, and either edit a copy, edit the current template, or create an empty template.
  • Edit away!  Drag/drop your printed content!

So, what’s it all add up to?

It’s very common for business applications to want to print the data they’ve collected, and it’s not hard to see how that might be a chore.  Silverlight 4 introduced the printing feature, which allows printing at a very low level, but it also allows a huge amount of freedom in determining exactly what gets rendered to the page.  This freedom can make simple printing tasks a chore.  My hope is that the CollectionPrinter helps demonstrate how one might build a general-purpose (but more constrained than the built-in API) printing API.  I’d love to know what you think.  For me, it was primarily an experiment to see how close I could come to making printing large data sets easy and designable.  Does this come close to what you’d hope for?  What would your ideal be?

Sweet!  Can I try it?!

I’m glad you’re so enthused!  I’m tempted to say no, but wouldn’t torture you like that :) .  You can give it a shot using my latest build of Silverlight and Beyond.

You can see the live demos here (Requires Silverlight 4):

Printing samples

If you’d like to save time printing (yeah, it takes a while – it renders some pretty large bitmaps!), you can see some sample output files below.  Notice that the “Long” documents don’t have the total page count displayed, since it took longer to pre-render all of the pages than the PrintPage retry limit would permit:

  • ItemTemplate-based Document – Short (PDF 7.5 MB), Long (PDF 37.7 MB)
  • DataGrid-based Document – Short (PDF 7.9 MB), Long (PDF 37.0 MB)
  • Individual page-based Document – Download (PDF 5.8 MB)

Finally, some source code for you:

  • Live Sample (source — found in the SLaB v0.5 source under "ScratchPrintingProject")
  • SLaB v0.5 (includes source, a sample app, some tests, and binaries)

    • For the latest version, please check out SLaB on my Downloads and Samples page.
    • The v0.5 download of SLaB includes the following changes:
      • Updated for SL4 RTW
      • Added CollectionPrinter for printing collections of items
      • Fixed a bug with Sitemap-based controls that caused some pack Uris to be evaluated as "equivalent" (and thus highlighted) even when they were not
      • Added EvenOddConverter that allows you to select an arbitrary value based on whether the input value was even or odd
      • Other minor bugfixes

    Enjoy, and let me know if you have any questions, thoughts, or ideas!

    Remember, SLaB is just a collection of the samples and experimental components I’ve been putting together so that they’re all in one place.  I can’t make any guarantees about maintaining them, fixing bugs, not making breaking changes, etc., but you’re more than welcome to try them out, use them, and let them inspire your development (or show you what not to do if you really dislike something I’m doing!) :) .

    P.S. Do you know you talk to yourself in these blog posts?

    Yes, yes I do, Mr. “Person behind the headers of the sections in my blog posts who is actually me.” 

     

    UPDATE 4/25/2010: Thanks for the feedback in the comments on this post.  I have updated the CollectionPrinter to help with the DataGrid AutoGenerateColumns issue reported by “mb”.  It may still have problems if the ItemsSource is not an IEnumerable<T> (but rather just a plain IEnumerable), but I think this should cover the 90% case.

    You can download the newest bits here.  I also added a nice little utility method to SLaB that helps get a MethodInfo using a compiled Expression (using the LINQ expression libraries).  Please let me know if you encounter other issues!

    , , , ,

    1. #1 by Sam Kea on April 16, 2010 - 5:26 pm

      cool. hope it allows grouping.

      • #2 by david.poll on April 16, 2010 - 5:41 pm

        If you’re using the DataGrid, the following should work:
        1. Use CollectionViewSource to group your items before passing the view into CollectionPrinter.ItemsSource
        2. In the BodyTemplate (as shown above), repeat the CollectionViewSource definition, causing the CurrentItems list to be grouped (it should end up being identical to the original grouping), and bind the DataGrid’s ItemsSource to this view.

        That should do it, I think! :)

        • #3 by Fama on August 4, 2010 - 2:27 pm

          I’m sorry, but I can’t figure out how to properly do step 2. I’ve tried putting the DataGrid inside a variety of containers and then putting the CollectionViewSource as a resource of that container, but this causes all the groups to be displayed on the first page (only 1 page document). I know I must be doing something simple incorrectly, but I just can’t figure it out.

          Just to be sure, the resource should look like this:

    2. #4 by Jalal on April 17, 2010 - 9:17 am

      Hey,
      Thx for this description about printing.
      In fact while i am using printing, I noticed that the size of the printed file is big. For example , just a canvas with some controls inside shows about 104 mb. Is that normal ?? or should i do something in order to get acceptable size.

      • #5 by david.poll on April 17, 2010 - 9:36 am

        Jalal,

        Unfortunately, the files are going to be quite big. This is because currently, Silverlight renders each page as a high-resolution bitmap rather than something built out of vector graphics and text. Most file formats that you might save to will compress the images. For an example, see the files I linked to at the bottom of the project. There, a 20-page document with tons of controls (Datagrid, charts with lots of data points, etc.) was about 37 MB.

    3. #6 by Jalal on April 17, 2010 - 9:53 am

      Thx David for your reply.

    4. #7 by leandro on April 17, 2010 - 9:57 am

      outstanding work, congrats
      just a quick question.
      how long did it take to get it all done ?

      • #8 by david.poll on April 17, 2010 - 10:14 am

        I spent a fair amount of time over the last few weeks, mostly tracking down a stubborn bug and educating myself about some of the nitty-gritty details of how/when things render.

    5. #9 by leandro on April 18, 2010 - 11:39 am

      thank you for sharing it, it will save me and everyone one a lot of time !

    6. #10 by Johan Blanckaert on April 20, 2010 - 6:24 am

      Is there a way to avoid the print dialog to appear an print directly to a specific printer? In a business application, I need to print out a barcode label as a user performs an action.
      Thanks,
      Johan

      • #11 by david.poll on April 20, 2010 - 10:57 am

        Johan,

        In Silverlight 4, the printing functionality does not support this type of interation-less printing. You will need to show the print dialog.

        You may be able to get around this by ineracting with the printer directly in an elevated out of browser application (through COM interop), but nothing built into Silverlight does this today.

        -David

    7. #12 by mb on April 21, 2010 - 2:18 pm

      This looks promising – keen to try it out.

      I am trying to use the new binaries (April 15).
      Have ref’d SLaB.Printing, SLaB.Utilities, and SLaB.Utilities.Xap
      and I keep getting runtime error:
      The type ‘BoolConverter’ was not found. [Line: 22 Position: 55]
      I can run the source code find though I notice the print test xaml page complains about this same converter not being found (but no runtime error).

      Any ideas?

      • #13 by david.poll on April 21, 2010 - 2:44 pm

        You shouldn’t need SLaB.Utilities.Xap — rather, SLaB.Utilities.Xaml

        Let me know if that fixes it!

        -David

    8. #14 by mb on April 21, 2010 - 3:17 pm

      Thanks for the quick reply.
      I recompiled your source in Release, and reduced the references as you suggested and so far so good! Thanks, look forward to testing this further now.

    9. #15 by Mike on April 22, 2010 - 12:58 am

      Great post David
      However I am wondering if you have a method for hiding/not showing print window. I am looking for posibility of printing on default printer right after user hits “Print” button. Any chances?

      • #16 by david.poll on April 23, 2010 - 11:14 pm

        As I said, in Silverlight 4, there’s no built-in way to do this. You might be able to accomplish it in an elevated out of browser app using COM automation, but it’s likely to be a hefty amount of work.

    10. #17 by Mike on April 22, 2010 - 1:00 am

      Sorry for questioning the same question as question #9 :-)

    11. #18 by Arun on April 22, 2010 - 4:14 am

      I want to print Master-Details records so I am using Datagrid and some textblocks in BodyTemplate. But it doesn’t show anything only empty page. How do i set DataContext in code-behind?

      • #19 by david.poll on April 23, 2010 - 11:13 pm

        Because the templates are DataTemplates, your DataContext will always be the CollectionPrintContext. Anything else you’d like to be able to access should be reachable via StaticResources or through the elements being passed into the ItemsSource.

        I’m not totally sure I understand your scenario, though. What is the structure you’re trying to get? What do you want on each page?

        • #20 by Arun on April 24, 2010 - 11:20 am

          Thanks for your reply.
          I want to print Invoice. Invoice no, date, total amount, customer name, address,etc., in the top and below line of items (SNo, Item no, description, qty, price). Master table has linked with details table. I am using entity framework master table has details table as navigation. I set Master table as CollectionPrinter’s DataContext and in the datagrid itemssource to Details table.

          • #21 by david.poll on April 25, 2010 - 12:45 am

            You can access the DataContext of the CollectionPrinter through the Host property on CollectionPrintContext. This gives you access to the control that is doing the printing. So, your binding within your HeaderTemplate might look like this: “{Binding Host.DataContext.InvoiceNumber}”

            Does that help solve the problem for you?

    12. #22 by mb on April 22, 2010 - 2:17 pm

      fyi – found a minor issue with the control… unfortunately I found it the painful way after a lot of trial and error! When using a datagrid in the BodyTemplate, when AutoGenerateColumns=True, the grid content is not printed. I was able to reproduce this in your sample project as well (changing AutoGenerateColumns=True and commenting the DataGrid.Columns).

      This is not really that important, I was just trying step by step when I ran into this. What I need to do is print a datagrid that has columns configurable by the user. So in fact I will not know the columns at design time, only runtime. Since the control works with a template, I guess I am stuck trying to concatenate strings to dynamically load the DataTemplate to be used by the Body template (I create the CollectionPrinter in code behind). So, for now I was cheating trying to use the autogenerate columns (figuring I could plug that in later) which is how I found this.

      • #23 by david.poll on April 23, 2010 - 9:41 pm

        Wow — thanks for reporting this — I totally missed it. It looks like the DataGrid isn’t auto-generating columns immediately when the collection begins getting items. I’ll look into this and see if there’s a clean solution.

        Thanks for reporting it!

      • #24 by david.poll on April 25, 2010 - 6:18 pm

        Alright, I think I’ve fixed the majority of the problem. Basically, I was using an

        ObservableCollection<object>

        to hold the CurrentItems in the CollectionPrintContext, which caused the DataGrid not to generate columns when the collection started out empty. Now, if the ItemsSource is an

        IEnumerable<T>

        , I produce an

        ObservableCollection<T>

        in the CollectionPrintContext.

        You can download the latest bits and give it a shot :)

    13. #25 by Arun on April 23, 2010 - 10:36 pm

      Any answer to my question? I had a datagrid with fixed columns (not auto-generated) .
      thanks

    14. #26 by mb on April 28, 2010 - 8:08 am

      David – no luck with new binaries – the column headers print, and what seems like the correct number of pages, but the rows are blank! (and this is for defined columns, not autogenerated)… very strange. I have restored the previous version for now, since I wrote the code to assemble the datatemplate dynamically anyway so autogeneratecolumns is not required,

      • #27 by david.poll on April 28, 2010 - 9:51 am

        Strange… Is it working with the sample in the source? That seems to be working for me without problems (with auto-generated columns).

    15. #28 by Pykaso on May 3, 2010 - 1:18 pm

      Hi, it is possible to assign ItemsSource of CollectionPrinter (MainCustomPrinter) to existing DomainDataSource and print data from existing DataGrid ?

      Thanks

    16. #29 by Braulio on May 8, 2010 - 2:15 am

      Hey David,

      Great article, something to consider by the toolkit chaps to add it, it will save tons of works and reinventing the wheel to may developers.

      In my case I’m taking a look to portrait / land scape printing + paper sizes and DPI’s seems that there’s no much we can do with tat?

      Cheers
      Braulio

    17. #30 by Mike on June 2, 2010 - 3:01 am

      Hi David,

      first congrats on great post.
      But there is always a but :-) .pdf samples which you have uploaded have good quality, but when I am printing from live demo to .pdf with Primo pdf printer the quality is very poor. So could you tell me how have you reached this good quality on .pdf prints :-) Thanks in advance. Mike

    18. #31 by Carlos on June 11, 2010 - 4:45 pm

      Hello David, what a great post.

      Do you know it’s possible to supress the printer dialog?
      Thanks in advanced
      KR
      Carlos

    19. #32 by Przemek on July 1, 2010 - 2:03 pm

      Hi, I am using SlaBv0.85. I have adapted your sample to my scenario with a DataGrid displaying people and their data. Everything works great, pages get rendered properly in the PrintingPage control. I can limit the number of items to print and select a page for viewing. However, when I hit print and type in the file name the BusyIndicator starts ‘counting’ the pages being printed and always stop on the last page. Then it never finishes.
      Any ideas what the problem might be?

      • #33 by david.poll on July 6, 2010 - 12:20 pm

        Przemek,

        Sorry for the delayed response — I’ve been on a vacation for the last 10 days.

        It’s hard to know without taking a look at the example. Do you have a repro that you could send me? Please send it to “david dot poll at microsoft dot com”, and I’ll try to help you out :)

        -David

    20. #34 by Nk54 on July 12, 2010 - 5:32 am

      This is my favorite library in SLaB :)

      It save me dozen hours of work ! Thanks a lot David !!

      • #35 by Nk54 on July 12, 2010 - 5:35 am

        as Braulio said, land scape printing would be a nice feature to add :)

        • #36 by david.poll on July 25, 2010 - 10:58 am

          I’ll consider it :)

          • #37 by Fama on August 3, 2010 - 2:39 pm

            I’ve begun testing this printing library for my project, but I need to generate many reports in landscape mode. Would this be something that I could implement relatively easily by modifying some of your code? Wouldn’t I just have to specify the printable area somehow?

            If you have any pointers for this I would greatly appreciate it.

    21. #38 by Greg on July 14, 2010 - 3:06 pm

      Hi David,

      I just watched your teched north video regarding OOB.

      Question: If I choose to have the app run OOB, does that mean I lose cross-platform/browser independence?

      That’s what I gathered from watching the video, but wanted you to confirm please.

      thanks much,

      Greg

      • #39 by david.poll on July 14, 2010 - 3:17 pm

        Greg,

        Nope — sorry if I was unclear in the presentation. OOB does not impact cross-browser/platform. However, there are a few Windows light-up features, such as COM Automation, which will only work on Windows. If you avoid those (or turn them off depending on whether the functionality is available), you can still have that app work across platforms.

        There are also some OOB features that are not available in-browser at all. However, your app can simply disable those features while running in-browser, and e.g. prompt the user to install the app if they want those features.

        -David

      • #40 by david.poll on July 25, 2010 - 10:57 am

        Greg,

        Sorry for the delay, I was just a bit busy creating my next blog post :)

        If you’re runing OOB, you can still be installed across platform/browser. There are a few features that don’t work across platforms (most importantly COM automation), but you can query for their support and turn off your functionality that requires them when appropriate.

        -David

    22. #41 by Vinod on July 18, 2010 - 11:08 pm

      Hi David,

      Thanks for the Article. Is it possible to generate a pdf document through PrintDocument in Silverlight 4 ?

      • #42 by david.poll on July 25, 2010 - 10:55 am

        Vinod,

        There’s not a built-in way to do this with the PrintDocument in Silverlight 4. However, if you have a PDF Printer (e.g. from Adobe, BullZip, or FoxIt), you can print the content to PDF.

        -David

    23. #43 by Dagaric on July 25, 2010 - 1:46 pm

      Hi David,
      Whether there is a possibility to clear memory usage after printing pages?
      For example if to print the large collection memory used by the application very strongly increases and then does not decrease.(even if I force GC).
      or it’s sl4 big memory leak bug?

      • #44 by david.poll on July 25, 2010 - 3:27 pm

        I suspect the latter, since the bug is around inline DataTemplates. I’ll look into it if I get a chance.

    24. #45 by Michael on August 5, 2010 - 8:49 am

      Hello David,
      First of all I like what you did with the printing api.

      I have a question though. I have a pretty large data source which i want to bind to the CollectionPrinter. The source is a little complex and am trying to print it in an itemscontrol format. And here is where my problem comes from. I can only see the first element in the Collection. I checked it and it has 5 elements.

      I am doing something like this, correct me if I am wrong:

      …….

      and then ofcource

    25. #46 by Michael on August 5, 2010 - 8:53 am

      sry about the previous comment:

    26. #47 by amrish on August 10, 2010 - 5:16 am

      Hi david,

      I have to take a print out of a control which is surrounded by a border control, internally it has itemcontrol which has data binding. and then itemtemplate and itemspanel is defined.

      issue is this is very big control which doesnt fit into a single page for printing. the output of this control is like inverted version of “periodic table” in chemistry.

      how can i take print out of this control

    27. #48 by Fama on August 11, 2010 - 12:32 pm

      I have a question regarding the possible grouping capability using your api. I can’t figure out how to properly do step 2 of your post on April 16th. I’ve tried putting the DataGrid inside a variety of containers and then putting the CollectionViewSource as a resource of that container, but this causes all the groups to be displayed on the first page (only 1 page document).

      Just to attempt to clarify what you meant, should my code in the BodyTemplate DataTemplate look like this:

      The above does not perform any pagination.

      Please advice if you have the time.
      Thank you.

      • #49 by Fama on August 25, 2010 - 11:00 am

        Please, if you are not too busy I would really appreciate some further information regarding grouping. I would really like to use your method of printing a document, but I can’t figure out how to properly group the items and still have the printer create multiple pages when needed. It’s as if it doesn’t know that it has reached the bottom of the page.

        • #50 by Helly on October 13, 2010 - 7:49 am

          I’m pretty much in the same situation as you Fama, David, do you have any insight on the issue?

    28. #51 by Fama on August 11, 2010 - 12:34 pm

      Sorry, I didn’t have the code tags in the last comment. Here is the code that I was trying to post (I hope it works):

      ...

      ...

    29. #52 by Fama on August 11, 2010 - 12:36 pm

      I guess the code tags still don’t let code with angle brackets through. Here is the code without angle brackets.

      Grid
      Grid.Resources
      CollectionViewSource Source=”{Binding CurrentItems}” …

      /CollectionViewSource
      /Grid.Resources
      sdk:DataGrid ItemsSource=”{Binding Source={StaticResource collectionName}}”

      /sdk:DataGrid
      /Grid

    30. #53 by jakob on September 9, 2010 - 4:52 am

      Hi David can I please get you to look at this:

      http://stackoverflow.com/questions/3653923/davidpolls-printcollection

      I’m having some problems with the printercollection, and I thought that you’d probably figure it out right away.

      Thank you so much for your coding-expertise :D

    31. #54 by Debashish Gupta on September 27, 2010 - 1:26 am

      I am trying to do Printing in silverlight using SLabs from http://www.davidpoll.com/

      I downloaded the SLaBv0.6
      I see 3 sub folders in it.
      Binaries , Build Utils , Source

      How should i use the Printing application in my silerlight solutions.

      I tried to just add reference of SLaB.Printing.Controls, SLaB.Utilities, and SLaB.Utilities.Xap from Binaries folder in my solution Application.

      and tried to use it in my XMAl page like this.

      xmlns:SLaB=”http://www.davidpoll.com/SLaB”

      But it gives me error like

      Header Template Not Found.

      Can some body suggest me any idea.

      Regards
      Debashish.

      • #55 by david.poll on September 30, 2010 - 11:49 pm

        Debashish,

        Please try using the latest version of SLaB from the downloads and samples page.

        -David

    32. #56 by Krunal Jariwala on October 5, 2010 - 3:23 am

      Nice solution,

      I download the SLaBv0.9

      I want to display data pager instead of numeric up down control for navigating between pages. Is there any method to do that?

    33. #57 by herzmeister der welten on October 14, 2010 - 4:13 am

      Nice work. It’s a shame that printing multiple pages is so complicated and counter-intuitive out-of-the-box.

      One bug I found in the demo app:

      Choose one of the print demo pages -> Click “Print” -> Click “Cancel” in the OS print dialog -> Click “Print” again -> Error “Cannot print two thingies at the same time.”

      Catching and handling print cancelling correctly is another tragedy in Silverlight 4 I suppose.

    34. #58 by John on October 20, 2010 - 11:23 am

      I hate to ask the obvious but…
      Is SLaB unrestricted by any copyright consiterations?

    35. #60 by John on October 24, 2010 - 12:15 pm

      I just don’t get it.
      I am in Expression blend in my project and want to create a new xaml page for the printer. However, I don’t see what to select (User control, Page?) and when I select Page, I cannot make it a Collection ptinter at the root as your example picture shows. AUGH!

      • #61 by david.poll on October 24, 2010 - 3:24 pm

        Unfortunately, I haven’t provided an item template (for use in Blend/VS) that would make the “CollectionPrinter at root” scenario super-easy. This is something I may consider in the future. In the meantime, you just need to change things in a few places (starting from a new UserControl) to make this work:

        In .xaml:

        <UserControl ...>...</UserControl>

        becomes

        <SLaB:CollectionPrinter xmlns:SLaB="http://www.davidpoll.com/SLaB" ...>...</SLaB:CollectionPrinter>

        In .xaml.cs:

        public partial class MyUserControl: UserControl

        becomes

        public partial class MyUserControl: CollectionPrinter

        I hope that helps!

    36. #62 by John on October 24, 2010 - 3:37 pm

      Think I figured out my previous question. Set up a user control called Print then added the Collection printer to it. Compiles and everything.
      BUT:
      How do I get it to do a print preview? I have a button who event activates print this way:
      Print p = new Print();

      and it does show a dialog, but not a print preview. Can’t find a print preview function when I use intellisense either. The xaml code doesn’t show a function, just mentiones preview in the constructor.

      Sorry to be a pain but this is the first time I am importing a DLL.

      • #63 by david.poll on October 24, 2010 - 7:29 pm

        The CollectionPrinter is a real control, and can be used as a pseudo-print preview (just put it somewhere in your visuals). However, Silverlight doesn’t support real print previews, so it’s really just a “best guess” of what the pages will look like.

    37. #64 by Fama on October 27, 2010 - 6:29 am

      Still working on the grouping issue, but I have made some progress on it. I have narrowed down the issue down to the refreshing of the CollectionViewSource. Since the CurrentItems is bound to the CollectionViewSource it seems to update it just fine, but the binding between the CollectionViewSource and the DataGrid is not updating until all the items are added to the CollectionViewSource.

      To test my theory on this I’ve added an ICollectionView to the CollectionPrintContext and hope that by refreshing the view in the CalculateChild method in CollectionPrinter at the right time it will redraw the grid. However, I’m not sure how to access the CollectionViewSource in the BodyTemplate from my CollectionPrinter’s constructor.

      I will keep trying various things along this line of though and post again if I find anything useful.

      • #65 by Fama on October 28, 2010 - 6:19 am

        Success!

        I added a CollectionViewSource to the CollectionPrintContext, added code to the CalculateChiild to create a new CollectionViewSource for each page, add the Group and Sort Descriptors to match one passed in, and then tie it’s source to the pageItems (similar to CurrentItems). Further down where it does the NotifyPropertiesChanged I also forced a refresh of the CollectionViewSource’s View. In the CollectionPrinter xaml I tied the DataGrid’s ItemsSource to {Binding DisplayCollectionViewSource.View}

        If you want I can give you the code that I used with a sample for you to update yours. It doesn’t affect the functionality of any other part of the printer and it adds the ability to bind to the CurrentItems or the DisplayCollectionViewSource.View

        • #66 by Mike on November 3, 2010 - 10:03 am

          Fama,

          Do you have code that you would be willing to share that supports grouping via collectionView? I have run into the same problem.

        • #67 by Mike on November 3, 2010 - 10:04 am

          Fama,

          Would you be willing to share the code? I am running into the same grouping problems.

        • #68 by Mike on November 3, 2010 - 11:51 am

          Sorry for the multiple posts. What I am really interested in is the code that enables printing muplitple pages with the collectionview set as the items source. I have it grouping properly, but once the collectionView is used instead of an observableCollection, multiple pages are no longer printed. David, any help you can provide would be great. Fama, code or direction from your solution would be great as well. Thanks.

    38. #71 by Fama on November 2, 2010 - 12:14 pm

      Is there a way to override the PrintableArea and PageMargins? I can do so for the print preview without a problem, but the actual printer one is overriding my values with the max values from the printer I have selected. Just setting my own margins would be enough for what I need.

    39. #72 by John on November 5, 2010 - 2:42 pm

      Almost got it. I used your example of Lorem Ipsum, took the text out and named those boxes. But, now I can’t get to the textboxes as I normally would wth code like this:
      public void SetText(string title, string body)
      {
      titleTextBlock.Text = title;
      bodyTextBlock.Text = body;
      }

      • #73 by david.poll on November 11, 2010 - 5:22 pm

        Unfortunately, I don’t think named textboxes are accessible easily from Code-behind in this way. Your best bet is to bind them to something rather than trying to push data in the other direction.

    40. #74 by hungpham on November 7, 2010 - 11:56 pm

      dear Mr.David
      I would like to ask, now i don’t want to show dialog printing.I want when i click on button ‘print’, then my document is immediately printing without dialog printing.
      regards

    41. #76 by SLGeek on November 12, 2010 - 10:57 am

      Can I directly send a set of documents to print without rendering it on the UI element ?

      • #77 by david.poll on November 12, 2010 - 11:01 am

        Not sure I completely understand what you’re asking — you must use a UIElement to render pages, but that doesn’t necessarily have to appear on the screen.

      • #78 by SLGeek on November 12, 2010 - 11:15 am

        I want to send the documents directly to the printer. I don’t want to use Browser printer like capability. The documente are huge in number. can I do that ?

        • #79 by david.poll on November 12, 2010 - 11:22 am

          As far as I know, unless you’re willing to go figure out how to use COM in an elevated trust out of browser application to do that communication, there’s no way to do that.

    42. #80 by SLGeek on November 12, 2010 - 11:22 am

      To further clarify my question, I have 1000 pages of document to be printed over the network. I am just evaluating if I can stream the documents and send it directly to the printer for printing.

    43. #81 by Shane on November 16, 2010 - 8:33 am

      I have, hopefully, a dumb question from a Silverlight newbie. I am trying to use this library to create a custom control that prints a DataGrid. The control is being used in a Lightswitch application. When I try to build my control I get the error:

      “The “CreateExtmap” task has been declared or used incorrectly, or failed during construction. Check the spelling of the task name and the assembly name.”

      Any idea what I’m doing wrong ? Thanks.

      • #82 by david.poll on November 16, 2010 - 9:06 am

        This sounds like you’re trying to build straight out of my projects, but have only grabbed the source folder. The MSBuild files in my projects reference a CreateExtmap task in the Tools folder that allows them to be used with assembly caching.

        • #83 by Shane on November 16, 2010 - 9:21 am

          My folder structure is the following:

          ApplicationName>LightSwitchProjectName
          OtherProjects
          SLaB> Binaries
          BuildUtils
          Source

          I then just added the projects Printing.Controls, Utilities, and Utilities.Xaml from the Source folder to my solution.

          Is there something else I have to do ?

        • #84 by ozinus on February 10, 2011 - 10:12 am

          I have a chart I want to print but I need to change its dimensions before printing (actually width) to fit it onto the screen. Now, When change the UI elements size to fit on the page and try to make it go back. There is this flicker on the screen. How can I achieve this gracefully.

          Thanks,

          • #85 by david.poll on February 10, 2011 - 10:21 am

            The only way to avoid the flicker as far as I know is to print elements that are not on the screen (i.e. re-create the chart off-screen and print that instead of what you’re actually displaying).

      • #86 by Shane on November 16, 2010 - 11:21 am

        Maybe I just need a quick rundown of how to actually use the library.

    44. #87 by Fama on December 3, 2010 - 4:39 pm

      If you guys are looking for report printing and are willing to pay for it, then I recommend taking a look at http://www.stimulsoft.com/ReportsSilverlight.aspx

      Their Silverlight client-side report generation is very powerful and much easier to setup and use.

    45. #88 by mitkoka on January 12, 2011 - 6:51 am

      Hello David,
      Thank you very much for your code. I use your control with a template that I set on the Style property in code behind. When I print a small amount of data everything is fine, but when I increase the number of the records I get:
      Index was out of range.
      “Must be non-negative and less than the size of the collection.”
      here
      e.PageVisual = this.pagePrinters[currentlyPrintingPage];

      I suppose it is somehow related to my template but I don’t really understand how.
      Any idea what’s wrong. Thanks.

    46. #89 by Ronnie Overby on February 24, 2011 - 7:01 pm

      David, this control is amazing. Thanks a lot!

      Is there a way to achieve printing 2 datagrids, one after the other, where each datagrid could span multiple pages?

      Thanks.

    47. #90 by Stijn Liesenborghs on March 10, 2011 - 7:36 am

      Hi David,

      Your control is great!!!!

    48. #91 by Rob on March 23, 2011 - 12:52 pm

      David,

      Do you plan on adding support to print in landscape. I’m working with your code trying to add support myself. I’m pretty close but I can’t figure out one thing. My landscape report gets cut off on the right side.

      I put in a property for landscape that will switch the printable width and height on Calculate, and then add a rotate transform at half of the printable width / 2 to the CollectionPrinter. I’ve tried setting the rotate transform in all parts of the code I could find to place it. I’ve also tried all kinds of stuff with the Measure method and the UpdateLayer method in all different places.

      My datagrid gets cut off the right side and the right most parts of the header and footer get cut off also. The landscape height and width seem fine while debugging. I’ve tried different printers with no luck.

      I’m so close yet so far away. It seemed simple enough but I’ve been working at it for days and that part keeps getting cut.

      Any help??

      • #92 by Rob on March 23, 2011 - 2:01 pm

        Ok, a few hours later I got it working. It was a simple mistake. Instead of applying the rotate transform to the CollectionPrinter, it must be applied to the CollectionPrinter._VisualChild.

        • #93 by madben2000 on October 20, 2011 - 7:04 am

          Do you have a solution for printing landscape?

    49. #94 by dchamba on April 1, 2011 - 9:10 am

      How to print Horizzontally ???

      Anyway..Good Job !!

    50. #95 by megann on April 17, 2011 - 2:19 am

      Do you have a sample output for this? Don’t you think it’s a bit risky to just print out the file without the accurate preview?

    (will not be published)



    Please wrap all source code with [code][/code] tags.