0

XAML を使用して、ヘッダー グリッドと可変行数を持つ印刷フォームを生成する必要があります。行数が増えると複数ページになる可能性があります。ヘッダーは各ページに表示する必要があり、行内でテキストが折り返されるため、各行の高さが異なる場合があります。現在、ItemsControl (行コンテナー) の ActualHeight を使用して新しいページを生成するタイミングを決定しようとしていますが、ActualHeightの値は常にゼロです。

私の「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 ビュー ページごとに入れ子になった ViewModel があります。各ページの ViewModel は、行オブジェクトのバッキング List を使用します。

   List<RowClass> RowsList;

ViewModel には、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;

最初の Measure/Arrange、maxRowsHeight の期待値を提供します。生成されたフォームは、数行の単一ページでは問題ないように見えます。私の特定の問題は次のとおりです。そして、一般的に、この問題に対するより良いアプローチはありますか?

4

1 に答える 1

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;
}
于 2013-01-24T16:16:32.740 に答える