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).
1条答案
按热度按时间bgtovc5b1#
所以这并不像我所期望的那样简单,如果不是偶然发现这篇文章,我可能永远也不会找到解决方案。在这篇文章中,作者创建了一系列类和控件,包括一个"FlowDocument友好"列表,并提供了将其呈现为Table的选项,这意味着必要时将在项的TableRows之间出现分页符。
下面是我更新的"报告视图" xaml。注意,我还通过使用
<Paragraph>
而不是BlockUIElement中的TextBlock重构了文本。您还会注意到,这些Paragraph也是可绑定的,通过自定义的BindableRun
控件(也是上述文章的一部分):自定义的
ItemsContent
控件用于呈现列表,它具有一些熟悉的属性:ItemsSource
属性ItemTemplate
属性,它定义一个由"Fragment"元素组成的DataTemplate(同样来自上面的文章),在 * this * 中还有一个<TableRow>
,用于呈现VM集合中的字符串项ItemsPanel
,用于定义"容器"-在本例中是一个Table
控件。此示例包括一个硬编码的TableRow,用于显示列标题。我相信其他容器也是允许的,例如,尽管我没有详细阅读这篇文章。无论如何,这一切看起来都很有效,并生成了一个正确分页的报告,字符串列表在正确的位置上分页。
如果其他人也像我一样使用本文生成报表,请注意
ItemsContent
Loaded事件不会触发,从而导致列表无法呈现(这可能是由于我以编程方式创建视图和/或未在屏幕上显示视图的方式)。为了使其正常工作,我必须注解掉本类后面出现的三行if (IsLoaded)
。