Is it a must to put Control inside Container to get the Template applied?

Emon Haque 3,176 Reputation points
2020-10-09T15:29:42.237+00:00

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?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,783 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Emon Haque 3,176 Reputation points
    2020-10-09T22:10:20.747+00:00

    Now with alternating row color, a report of 132 pages (3600 entries in total), takes about 30-35 seconds to finish the task, if provide the file name and hit save quickly, with these:

    void print(object o)
    {
        var dialog = new PrintDialog();
        if (dialog.ShowDialog() != true) return;
        var watch = new Stopwatch();
        watch.Start();
        var size = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
        var doc = new FixedDocument();
        doc.DocumentPaginator.PageSize = size;
        int pageNo = 0;
        ReportPage pageControl = getPage(doc, ref pageNo);
        for (int i = 0; i < items.Count; i++)
        {
            if (pageControl.AddItem(items[i]) > pageControl.AvailableHeight)
            {
                pageControl.RemoveItem(items[i]);
                pageControl = getPage(doc, ref pageNo);
                pageControl.AddItem(items[i]);
            }
        }
        dialog.PrintDocument(doc.DocumentPaginator, "");
        Debug.WriteLine($"Time taken: {watch.ElapsedMilliseconds} ms");
    }
    
    ReportPage getPage(FixedDocument doc, ref int pageNo)
    {
        var page = new ReportPage()
        {
            Width = doc.DocumentPaginator.PageSize.Width,
            Height = doc.DocumentPaginator.PageSize.Height,
            Title = "My Title",
            SubTitle = "My Sub Title",
            Date = "as at " + DateTime.Today.ToString("dd MMMM, yyyy"),
            FootNote = "A Report",
            PageNo = ++pageNo
        };
        var grid = new Grid() { Children = { page } };
        grid.Measure(new Size(page.Width, page.Height));
        grid.Arrange(new Rect(grid.DesiredSize));
        doc.Pages.Add(new PageContent() { Child = page.Page });
        return page;
    }
    

    is that normal?

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.