Moving forward from the original problem, I've simplified the process of reporting with FixedPage
BUT one thing I still don't understand, that is: why do I have to put my custom control inside a grid? Here's what I've to do to print now:
void print(object o)
{
var dialog = new PrintDialog();
if (dialog.ShowDialog() != true) return;
var size = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
var pageControl = new ReportPage()
{
Width = size.Width,
Height = size.Height,
Title = "My Title",
SubTitle = "My Sub Title",
Date = "as at " + DateTime.Today.ToString("dd MMMM, yyyy"),
FootNote = "A Report",
PageNo = 1
};
//pageControl.ApplyTemplate();
//pageControl.UpdateLayout();
//pageControl.Measure(size);
//pageControl.Arrange(new Rect(pageControl.DesiredSize));
var grid = new Grid() { Children = { pageControl } };
grid.Measure(size);
grid.Arrange(new Rect(grid.DesiredSize));
var doc = new FixedDocument() { Pages = { new PageContent() { Child = pageControl.Page } } };
doc.DocumentPaginator.PageSize = size;
var item = new Item()
{
Date = DateTime.Today,
Particulars = "Some text",
Receivable = 10000,
Receipt = 0,
Balance = 10000
};
pageControl.AddItem(item)
dialog.PrintDocument(doc.DocumentPaginator, "");
}
The ReportPage
is the custom control with FixedPage
as root element and it has Header, Footer and rooms for Page Total as well as Content. When I create the FixedDocument
, doc, I put the FixedPage
, pageControl.Page, as the child and there's no reference of preceding grid in the doc
BUT to get rid of the null
exception, I've to put it in the Grid
, grid.
When I put it in the grid
and call Measure
and Arrange
, the OnApplyTemplate
of the ReportPage
is called and everything there initialize. I've tried to get rid of the intermediary grid
by using the commented out code BUT none of those calls the OnApplyTemplate
! Here's the relevant code of the custom control:
public class ReportPage : Control
{
ListBox content { get; set; }
public double AvailableHeight { get; set; }
public FixedPage Page { get; set; }
static ReportPage()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ReportPage), new FrameworkPropertyMetadata(typeof(ReportPage)));
}
public override void OnApplyTemplate()
{
Page = GetTemplateChild("page") as FixedPage;
content = GetTemplateChild("content") as ListBox;
var header = GetTemplateChild("header") as StackPanel;
var contentGrid = GetTemplateChild("contentGrid") as Grid;
var subTotal = GetTemplateChild("subTotal") as Border;
var footer = GetTemplateChild("footer") as Border;
double margin = 96d;
Page.Margin = new Thickness(margin);
AvailableHeight = contentGrid.Height = Height - 2 * margin - heightOf(header) - heightOf(subTotal) - heightOf(footer);
contentGrid.Width = Width - 2 * margin;
}
double heightOf(UIElement element)
{
element.Measure(new Size(Width, Height));
element.Arrange(new Rect(element.DesiredSize));
return element.DesiredSize.Height;
}
public double AddItem(Item item)
{
content.Items.Add(item);
content.UpdateLayout();
TotalReceivable += item.Receivable;
TotalReceipt += item.Receipt;
return content.DesiredSize.Height;
}
public void RemoveItem(Item item)
{
content.Items.Remove(item);
content.UpdateLayout();
TotalReceivable -= item.Receivable;
TotalReceipt -= item.Receipt;
}
}
Is it a must to put Control
in some sort of containers like Border
, Grid
, StackPane
, DockPanel
, etc. for OnApplyTemplate
to be called? Or I can derive from something else, other than Control
, or call some other function to get rid of the intermidiary gid
in print
function?