First off, I'm not sure if I phrased the title correctly. There are UserControls
which are added via a ViewModel
and I find them by searching the VisualTree
and add them to a ObservableCollection<Grid>
. What I would like to do is Print each instance of the UserControl
that I retrieve from the VisualTree
into a FixedDocument
but with each UserControl
being on a single page until it fills that page and moves on to the next page.
Here is the code for my current Print Button Click Event:
private async void btnPrint_Click(object sender, RoutedEventArgs e)
{
try
{
if (tabMain.Items.Count > 0)
{
tab = new TabLayout();
tab.Payslip = new ObservableCollection<Grid>();
foreach (var grid in FindVisualChildren<Grid>(this))
{
if (grid.Name == "pdfFile")
{
tab.Payslip.Add(grid);
}
}
FrameworkElement toPrint = new FrameworkElement();
PrintDialog printDialog = new PrintDialog();
PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
Size visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
FixedDocument fixedDoc = new FixedDocument();
StackPanel panel = new StackPanel(); //was trying to stack them in a stackpanel first but it threw an exception about same instance of usercontrol blah blah...
foreach (var doc in tab.Payslip.ToList())
{
double yOffset = 0;
doc.Measure((new Size(double.PositiveInfinity, double.PositiveInfinity)));
doc.Arrange(new Rect(new Point(0, 0), doc.DesiredSize));
Size size = doc.DesiredSize;
while (yOffset < size.Height)
{
VisualBrush vb = new VisualBrush(doc);
vb.Stretch = Stretch.None;
vb.AlignmentX = AlignmentX.Left;
vb.AlignmentY = AlignmentY.Top;
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.TileMode = TileMode.None;
vb.Viewbox = new Rect(0, yOffset, visibleSize.Width, visibleSize.Height);
FixedPage page = new FixedPage();
PageContent pageContent = new PageContent();
((IAddChild)pageContent).AddChild(page);
fixedDoc.Pages.Add(pageContent);
page.Width = fixedDoc.DocumentPaginator.PageSize.Width;
page.Height = fixedDoc.DocumentPaginator.PageSize.Height;
Canvas canvas = new Canvas();
FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
canvas.Width = visibleSize.Width;
canvas.Height = visibleSize.Height;
canvas.Background = vb;
page.Children.Add(canvas);
yOffset += visibleSize.Height;
}
}
//printDialog.PrintDocument(fixedDoc.DocumentPaginator, "");
ShowPrintPreview(fixedDoc);
}
}
catch(Exception ex)
{
var exceptionDialog = new MessageDialog
{
Message = { Text = ex.ToString() }
};
await DialogHost.Show(exceptionDialog, "RootDialog");
}
}
This is how it looks when I try printing three Tabs (UserControls are hosted in Tabs): As you can see here it prints three separate pages
I want all three on one page and if I print 10 tabs, then it should fill the first page and go on to the next page.
The last time I asked a similar question I was questioned on whether I wrote the code. Bits and pieces of this code came from similar FixedDocument
questions on StackOverflow
but it has been edited to the point where it actually works for me. So yes I know that the FixedPage
reference inside the foreach
is whats causing the creation of the three separate pages and I do understand the code.
Summary:
What I want to know is how to get the UserControls
from each Tab
, onto a single page until its full, without getting the "Specified element is already the logical child of another element. Disconnect it first." error.
I ended up using
ItemsControl
to hold myObservableCollection
ofControls
as a list and print from theItemsControl
using thisXpsDocumentWriter
method.PrintItemsTo(), displays a PrintDialog to print to any printer while WriteAllItems displays a Dialog to save as PDF.