How to get the Actual Height of content ListBox in FixedDocument

Emon Haque 3,176 Reputation points
2020-10-08T16:11:50.46+00:00

Looks like FixedDocument is the way to go to have multi-paged formatted document with rolling page subtotal. Since I'm still in the process of figuring out how to add page subtotal all my code is in one function print. Here're the steps:

  1. get the Size of the FixeedDocument/Page and set uniform margins.
  2. add header
  3. add footer
  4. determine the Content Area of the Fixed page by subtracting top/bottom margins and heights of header/footer from page Size
  5. instantiate ListBox for content items and put that in the Grid for content area
  6. add 2 content item and check the height of the content ListBox
  7. set position of the header, content area and footer in the Grid for the FixedPage, set row heights of the Grid accordingly and put all those in the Grid
  8. create FixedDocument, FixedPage and print.

this unnecessary line's been added to format code

void print(object o)  
{  
    var dialog = new PrintDialog();  
    if (dialog.ShowDialog() == true)  
    {  
        var size = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);  
        var margin = 96d;  

        var header = new Grid()   
        {   
            Width = size.Width - 2 * margin,   
            Children =   
            {   
                new ReportHeader() /*subclass of Control*/  
                {  
                    Title = "My Title",  
                    SubTitle = "My Sub Title",  
                    Date = $" as at {DateTime.Today.AddDays(5).ToString("dd MMMM, yyyy")}"  
                }  
            }   
        };  
        header.Measure(size);  
        header.Arrange(new Rect(header.DesiredSize));  

        var footer = new Grid() { Children = { new ReportFooter() /*subclass of Control*/ } };  
        footer.Measure(size);  
        footer.Arrange(new Rect(footer.DesiredSize));  

        var contentHeight = size.Height - (2 * margin) - header.DesiredSize.Height - footer.DesiredSize.Height;  
        var content = new ReportItems(); //subclass of ListBox  
        var contentArea = new Grid()   
        {   
            Height = contentHeight,  
            Children = { content }  
        };  
        contentArea.Measure(new Size(size.Width, contentHeight));  
        contentArea.Arrange(new Rect(contentArea.DesiredSize));  

        content.Items.Add(new Item()  
        {  
            Date = DateTime.Today,  
            Particulars = "Some text",  
            Receivable = 10000,  
            Receipt = 0,  
            Balance = 10000  
        });  
        content.Measure(size);  
        content.Arrange(new Rect(content.DesiredSize));  
        Debug.WriteLine($"Content Height : {content.DesiredSize.Height}");  

        content.Items.Add(new Item()  
        {  
            Date = DateTime.Today,  
            Particulars = "Some other text",  
            Receivable = 0,  
            Receipt = 5000,  
            Balance = 5000  
        });  
        content.Measure(size);  
        content.Arrange(new Rect(content.DesiredSize));  
        Debug.WriteLine($"Content Height : {content.DesiredSize.Height}");  

        Grid.SetRow(header, 0);  
        Grid.SetRow(contentArea, 1);  
        Grid.SetRow(footer, 2);  

        var pageVisual = new Grid()  
        {  
            RowDefinitions =  
            {  
                new RowDefinition(){Height = new GridLength(header.DesiredSize.Height)},  
                new RowDefinition(){Height = new GridLength(contentArea.DesiredSize.Height)},  
                new RowDefinition(){Height = new GridLength(footer.DesiredSize.Height)}  
            },  
            Children = { header, contentArea, footer }  
        };  

        var doc = new FixedDocument();  
        doc.DocumentPaginator.PageSize = size;  
        var page = new FixedPage()   
        {  
            Height = size.Height,  
            Width = size.Width,  
            Margin = new Thickness(96),  
            Children = { pageVisual }  
        };  
        doc.Pages.Add(new PageContent() { Child = page });  
        dialog.PrintDocument(doc.DocumentPaginator, "");  
    }  
}  

I've 2 issues, first, header and footers are custom control and subclass of Control, If I add those directly in the pageVisual grid, those don't appear in the document! So I've put both of those in Grid to see them in my document. What to do to be able to add them directly in the page Grid?

and second, after adding each item in the ListBox, content, I called Measure and Arrange on content and here's the output:

Content Height : 17.96  
Content Height : 17.96  

Why the height doesn't grow as I add item in the ListBox? How can I get the actual height of the ListBox after adding an Item?

EDIT

Here's the output:

31093-capture.png

Title, Sub Title, Date and Table Header are part of Header custom control. Line and two pieces of Text are part of Foother custom control.

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,691 questions
0 comments No comments
{count} votes

Accepted answer
  1. Emon Haque 3,176 Reputation points
    2020-10-09T08:31:46.617+00:00

    I'd to call UpdateLayout before Measure and Arrange and set the Width of the ListBox when initialized. In addition to the ListBox height, I also can get the last added item's height and it probably is better to have all those code in the extended ListBox like this:

    public class ReportItems : ListBox  
    {  
        public double LastItemsHeight { get; set; }  
        public double ListBoxHeight { get; set; }  
    
        static ReportItems()  
        {  
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ReportItems), new FrameworkPropertyMetadata(typeof(ReportItems)));  
        }  
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)  
        {  
            UpdateLayout();  
            var lastItem = ItemContainerGenerator.ContainerFromItem(e.NewItems[0]) as ListBoxItem;  
            LastItemsHeight = lastItem.ActualHeight;  
    
            Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));  
            Arrange(new Rect(DesiredSize));  
            ListBoxHeight = DesiredSize.Height;  
        }  
    }   
    

    with all those, now I can get those heights with:

    Debug.WriteLine($"Item height  {content.LastItemsHeight}, Listbox Height: {content.ListBoxHeight}");  
    

    To account for multiline Particulars in the ListBox, I've to set TextWrapping=Wrap for the TextBlock and disable the HorizontalScrollBarVisibility of the ListBox. here's the debug output:

    Item height  15.96, Listbox Height: 17.96  
    Item height  63.84, Listbox Height: 81.80000000000001  
    Item height  15.96, Listbox Height: 97.76000000000002  
    

    and here's the document:

    31184-capture.png

    Nice!

    0 comments No comments

0 additional answers

Sort by: Most helpful