Here is a solution. We are trying to separate the view concerns from the view model concerns, so there are still improvements to be made.
The CreatePages method in the report class is now:
private static IEnumerable<XAML_Form> CreatePages()
{
IList<XAML_Form> pages = new List<XAML_Form>();
int rowCount = sourceRowsObjectList.Count;
int remainingRowCount = rowCount;
do
{
var pageVm = new PageViewModelClass();
var page = new XAML_Form(pageVm);
pages.Add(page);
int numberOfRowsToAdd = Math.Min(remainingRowCount, XAML_Form.MaxNumberOfRows);
pageVm.AddRows(sourceRowsObjectList.Skip(rowCount - remainingRowCount).Take(numberOfRowsToAdd));
remainingRowCount -= numberOfRowsToAdd;
while (page.AreRowsOverflowing())
{
pageVm.RemoveLastRow();
remainingRowCount++;
}
} while (remainingRowCount > 0);
return pages;
}
The pertinent XAML_Form code behind is as follows:
private static int _maxNumberOfRows = -1;
public XAML_Form(PageViewModelClass viewModel)
{
InitializeComponent();
Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
Arrange(new Rect(new Point(0, 0), DesiredSize);
ViewModel = viewModel;
}
public PageViewModelClass ViewModel
{
get { return (PageViewModelClass)DataContext; }
private set { DataContext = value; }
}
public static int MaxNumberOfRows
{
get // Compute this only once, the first time it is called.
{
if (_maxNumberOfRows < 0) return _maxNumberOfRows;
var page = new XAML_Form();
var singleRowCollection = new object[] { null; }
page._rowsControl.ItemsSource = singleItemCollection;
page._rowsControl.UpdateLayout();
var rowHeight = page._rowsControl.ActualHeight;
_maxNumberOfRows = (int)((page.DesiredSize.Height - page._headerControl.ActualHeight) / rowHeight);
page._rowsControl.ItemsSource = null;
return _maxNumberOfRows;
}
}
// Call this method as rarely as possible. UpdateLayout is EXPENSIVE!
public bool AreRowsOverflowing()
{
_rowsControl.UpdateLayout();
return _rowsControl.ActualHeight > DesiredSize.Height - _headerControl.ActualHeight;
}