XAML 为FlowDocument分页

fv2wmkja  于 2022-12-07  发布在  其他
关注(0)|答案(1)|浏览(187)

I want to be able to create "reports" as regular MVVM views, so to test this idea I created the following view. It just consists of a bound textblock, an ItemsControl (bound to a collection of a few hundred strings), and a second text block:

<UserControl ...>
    <UserControl.Resources>
        <DataTemplate x:Key="myListItemTemplate">
            <TextBlock Text="{Binding}"
                       Foreground="Blue" />
        </DataTemplate>
    </UserControl.Resources>

    <FlowDocument x:Name="flowDoc">
        <BlockUIContainer>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding Message}" />
                <ItemsControl ItemsSource="{Binding SomeList}"
                              ItemTemplate="{StaticResource myListItemTemplate}" />
                <TextBlock Text="THE END" />
            </StackPanel>
        </BlockUIContainer>
    </FlowDocument>
</UserControl>

I print the view using a DocumentPaginator like this:

private void Print(IPrintableView view)
    {
        var dlg = new PrintDialog();
        if (!dlg.ShowDialog().GetValueOrDefault())
        {
            return;
        }

        var paginator = new MyDocumentPaginator(
            view.FlowDocument,
            new Size(dlg.PrintableAreaWidth, dlg.PrintableAreaHeight),
            new Size(DefaultMarginWidthDpi, DefaultMarginHeightDpi));

        dlg.PrintDocument(paginator, "My print job");
    }

Unsurprisingly the document isn't being paginated, and produce a 1-page report that gets truncated partway through the list. (I don't believe my document paginator implementation is important here, which simply adds a header and footer to each page, and I see the same issue if I use the FlowDocument's own FlowDocumentPaginator).
As I now understand it, document paginators will only break on " Block " controls, so I modified the xaml to wrap each control in a BlockUIContainer :

<UserControl ...>
    <UserControl.Resources>
        <DataTemplate x:Key="myListItemTemplate">
            <TextBlock Text="{Binding}"
                       Foreground="Blue" />
        </DataTemplate>
    </UserControl.Resources>

    <FlowDocument x:Name="flowDoc">
        <BlockUIContainer>
            <TextBlock Text="{Binding Message}" />
        </BlockUIContainer>
        <BlockUIContainer>
            <ItemsControl ItemsSource="{Binding SomeList}"
                          ItemTemplate="{StaticResource myListItemTemplate}" />
        </BlockUIContainer>
        <BlockUIContainer>
            <TextBlock Text="THE END" />
        </BlockUIContainer>
    </FlowDocument>
</UserControl>

Here, a page break now occurs after the first TextBlock, with the list starting on page 2 (but still being truncated at the end of that page), and the second textblock appearing on page 3.
I assume the paginator is measuring and rendering the entire ItemsControl as a whole, so I then tried placing each list item inside a BlockUIContainer via the ItemContainerStyle:

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <BlockUIContainer>
                        <ContentPresenter Content="{Binding}" />
                    </BlockUIContainer>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ItemsControl.ItemContainerStyle>

This made no difference though. Is there a way to get this working? Am I even approaching this in the right way? Placing every UI control inside a "block" seems very cumbersome, especially when it the time comes to implement a real report containing a lot of complexity. The real reports won't necessarily be list based either, and are more likely to contain a mixture of controls - text, images, lists, etc. in a fairly free-style layout (more web page than banded report).

bgtovc5b

bgtovc5b1#

所以这并不像我所期望的那样简单,如果不是偶然发现这篇文章,我可能永远也不会找到解决方案。在这篇文章中,作者创建了一系列类和控件,包括一个"FlowDocument友好"列表,并提供了将其呈现为Table的选项,这意味着必要时将在项的TableRows之间出现分页符。
下面是我更新的"报告视图" xaml。注意,我还通过使用<Paragraph>而不是BlockUIElement中的TextBlock重构了文本。您还会注意到,这些Paragraph也是可绑定的,通过自定义的BindableRun控件(也是上述文章的一部分):

<UserControl ...>
    <FlowDocument x:Name="flowDoc"
                  TextElement.FontFamily="Arial">
        <Paragraph Margin="0,0,0,20">
            <flowDocuments:BindableRun BoundText="{Binding Message}" />
        </Paragraph>

        <flowDocuments:ItemsContent ItemsSource="{Binding SomeList}">
            <flowDocuments:ItemsContent.ItemsPanel>
                <DataTemplate>
                    <flowDocuments:Fragment>
                        <Table>
                            <Table.Columns>
                                <TableColumn Width="*" />
                            </Table.Columns>
                            <TableRowGroup flowDocuments:Attached.IsItemsHost="True">
                                <TableRow Background="LightBlue">
                                    <TableCell>
                                        <Paragraph>Heading</Paragraph>
                                    </TableCell>
                                </TableRow>
                            </TableRowGroup>
                        </Table>
                    </flowDocuments:Fragment>
                </DataTemplate>
            </flowDocuments:ItemsContent.ItemsPanel>
            <flowDocuments:ItemsContent.ItemTemplate>
                <DataTemplate>
                    <flowDocuments:Fragment>
                        <TableRow>
                            <TableCell>
                                <Paragraph>
                                    <flowDocuments:BindableRun BoundText="{Binding}" />
                                </Paragraph>
                            </TableCell>
                        </TableRow>
                    </flowDocuments:Fragment>
                </DataTemplate>
            </flowDocuments:ItemsContent.ItemTemplate>
        </flowDocuments:ItemsContent>

        <Paragraph>THE END</Paragraph>
    </FlowDocument>
</UserControl>

自定义的ItemsContent控件用于呈现列表,它具有一些熟悉的属性:

  • 用于绑定到VM集合的ItemsSource属性
  • 一个ItemTemplate属性,它定义一个由"Fragment"元素组成的DataTemplate(同样来自上面的文章),在 * this * 中还有一个<TableRow>,用于呈现VM集合中的字符串项
  • 一个ItemsPanel,用于定义"容器"-在本例中是一个Table控件。此示例包括一个硬编码的TableRow,用于显示列标题。我相信其他容器也是允许的,例如,尽管我没有详细阅读这篇文章。

无论如何,这一切看起来都很有效,并生成了一个正确分页的报告,字符串列表在正确的位置上分页。
如果其他人也像我一样使用本文生成报表,请注意ItemsContent Loaded事件不会触发,从而导致列表无法呈现(这可能是由于我以编程方式创建视图和/或未在屏幕上显示视图的方式)。为了使其正常工作,我必须注解掉本类后面出现的三行if (IsLoaded)

相关问题