2013-01-09 30 views
0

我需要使用XAML生成一个打印的表单,该表单包含一个表头网格和可变数量的行,随着行数的增加可能导致出现多个页面。标题必须出现在每个页面上,并且每行的高度可能因行内的文本换行而有所不同。我目前正在尝试使用ActualHeightItemsControl(行容器)来确定何时生成新页面,但ActualHeight的值始终为零使用ItemsControl ActualHeight分页打印的XAML表单?

我的“XAML_Form”具有以下结构。 ItemTemplate中使用一个网格,允许将行中的列与标题网格中的列对齐。

<Grid Width="980" Height="757"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <Grid Name="_headerControl" Grid.Row="0"/> 
     <ItemsControl Name=_rowsControl ItemsSource={Binding Rows} Grid.Row="1"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Grid/> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </Grid> 
</Grid> 

我们的架构有一个报告类,用于处理添加页脚,页码以及将页面聚合到PDF中。此报告类为每个XAML View页面都有一个嵌套的ViewModel。该视图模型的每一页使用行对象的后备名单:

List<RowClass> RowsList; 

视图模型还具有用于绑定的ItemsSource时的ICollectionView:

ICollectionView Rows = new ListCollectionView(RowsList); 

我的报告类有一个CreatePages方法,包含这样的代码:

IList<XAML_Form> pages = new List<XAML_Form>(); 
var vm = new PageViewModelClass(); 
var page = new XAML_Form { DataContext = vm }; 
page.InitializeComponent(); 
page.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity); 
page.Arrange(new Rect(new Point(0,0), page.DesiredSize)); 
var maxRowsHeight = page.DesiredSize.Height - page._headerControl.ActualHeight; 
pages.Add(page); 
var rowsOnPage = 0; 
foreach (var row in sourceRowsObjectList) 
{ 
    rowsOnPage++; 
    vm.RowsList.Add(row); 
    vm.Rows.Refresh(); 
    page.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity); 
    page.Arrange(new Rect(new Point(0,0), page.DesiredSize)); 
    if (page._rowsControl.ActualHeight <= maxRowsHeight) 
     continue; 

    // The rows exceed the available space; the row needs to go on the next page. 
    vm.RowsList.RemoveAt(--rowsOnPage); 
    vm = new PageViewModelClass(); 
    vm.RowsList.Add(row); 
    rowsOnPage = 1; 
    page = new XAML_Form { DataContext = vm }; 
    page.InitializeComponent(); 
    pages.Add(page); 
} 
return pages; 

初始测量/安排为我提供MAXR的预期值owsHeight。生成的表单对于有几行的单个页面看起来很好。我的具体问题是:为什么page._rowsControl.ActualHeight总是为零?一般来说,是否有更好的方法来解决这个问题?

+0

我是通过WPF博士的一系列更深的挖掘到的ItemsControl。我发现“网格”不是项目控制类的主机项目之一。这与这有什么关系? –

+0

终于在这方面取得了一点进展。可以调用“非常气馁的”UpdateLayout方法。由于这是非常昂贵的,我不想在添加每一行后调用UpdateLayout。相反,我将添加应该放在页面上的行数,而不包装,然后调用UpdateLayout。如果它们不合适,则继续删除一行并调用UpdateLayout,直到行适合。仍然不喜欢这种方法,但它看起来会起作用。 –

回答

0

这是一个解决方案。我们试图将观点问题与视图模型问题分开,所以仍然有待改进。

报告类的CreatePages方法现在是:

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; 
} 

相关XAML_Form后面的代码如下:

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; 
} 
+0

我们已经进一步优化了这一点,以避免调用UpdateLayout。也许我会在某个时候发布。 –

+0

我们还通过在后面的代码中创建一个“虚拟面板”来优化此功能,该功能仅包含可以换行的文本框控件。然后,我们可以只在这个虚拟面板上调用UpdateLayout,它从不显示,并且不是表单的父对象,所以它更便宜。 –