我的应用程序中有一个FlowDocument
,如下所示:
<FlowDocumentScrollViewer Grid.Row="1">
<FlowDocument>
<Section>
<Paragraph>Header</Paragraph>
</Section>
<Section>
<Paragraph >
Footer
</Paragraph>
</Section>
</FlowDocument>
</FlowDocumentScrollViewer>
我想要的是将第一个<Section>
设置为文档的顶部作为页眉,第二个作为页脚保留在文档的底部。
有人能告诉我最好的方法吗?
答案 0 :(得分:2)
起点是显示how to wrap a DocumentPaginator
and add decoration to the pages的博客文章。限制是它旨在直接用于打印API。它不能直接与查看者一起使用,因为它们要求文档为FlowDocument
,并且无法覆盖子类中的DocumentPaginator
。
我为FlowDocumentPageViewer
找到的解决方案有几个部分。我没有尝试将其改编为FlowDocumentScrollViewer
,但可能使用相同的原则。
我首先使用Blend提取FlowDocumentPageViewer
的默认XAML模板。我实际上想要删除一些位,但页眉/页脚的关键更改是(作为统一差异)
- <Style TargetType="{x:Type FlowDocumentPageViewer}">
+ <Style TargetType="{x:Type local:CustomFlowDocumentPageViewer}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Uid="Border_47" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<AdornerDecorator ClipToBounds="True" Uid="AdornerDecorator_1">
- <DocumentPageView DocumentPaginator="{x:Null}" KeyboardNavigation.IsTabStop="False" DocumentViewerBase.IsMasterPage="True" PageNumber="0" KeyboardNavigation.TabIndex="1" KeyboardNavigation.TabNavigation="Local" Uid="DocumentPageView_1"/>
+ <DocumentPageView DocumentPaginator="{x:Null}" KeyboardNavigation.IsTabStop="False" DocumentViewerBase.IsMasterPage="True" PageNumber="0" KeyboardNavigation.TabIndex="1" KeyboardNavigation.TabNavigation="Local" Uid="DocumentPageView_1" x:Name="DocumentPageView_1"/>
</AdornerDecorator>
</Border>
由于你应该能够从XAML diff中解决问题,我的子类称为CustomFlowDocumentPageViewer
。由于FlowDocumentPageViewer
要求其Document
为FlowDocument
,因此尝试更改文档毫无意义。但我能做的就是拦截它并将其DocumentPaginator
包装在一个自定义的中,我将其注入DocumentPageView
。 XAML差异的后半部分是为了让我找到它。
private DocumentPageView _PageView;
public DocumentPaginator CustomDocumentPaginator { get; private set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_PageView = (DocumentPageView)GetTemplateChild("DocumentPageView_1");
if (_PageView != null) _PageView.DocumentPaginator = CustomDocumentPaginator;
}
protected override void OnDocumentChanged()
{
base.OnDocumentChanged();
var doc = Document as FlowDocument;
CustomDocumentPaginator = doc == null ? null : new DocumentPaginatorWrapper((doc as IDocumentPaginatorSource).DocumentPaginator, new Thickness { Bottom = 32 /* 0.333in */ });
if (_PageView != null) _PageView.DocumentPaginator = CustomDocumentPaginator;
}
当然,只有更改DocumentPageView.DocumentPaginator
才会出现一个大问题:如果您调用myCustomFlowDocumentPageViewer.Print()
,则会打印原始文档,而不会显示页眉和页脚。解决方案很简单但很长(约100行);如果您使用referencesource.microsoft.com
,您应该能够找出如何覆盖OnPrintCommand
以将CustomDocumentPaginator
发送到XpsDocumentWriter
而不是((IDocumentPaginatorSource)document).DocumentPaginator
。
我说起点是一篇博客文章,展示了如何包装DocumentPaginator
。它没有提到一些细微之处,可能是因为它打算用于打印文档一次,因此作者没有碰到它们。
DocumentPage
是一次性的。当您从包装的分页器中获取页面时,您有责任处理它。如果不这样做,您可能会发现,当您将包装好的网页ArgumentException: Specified Visual is already a child of another Visual or the root of a CompositionTarget
添加到Visual
时,您可以随意导航ContainerVisual
。ArgumentException
的另一种方法是在已经有页面的包装副本时尝试包装页面。例如。如果您打印,则会发生这种情况,因为XpsDocumentWriter
将请求当前正在呈现的页面副本。因此,您需要仔细管理页面的生命周期。我的paginator包装器的修改版本是
class DocumentPaginatorWrapper : DocumentPaginator
{
private Thickness _Margin;
private DocumentPaginator _Paginator;
// Required to avoid "ArgumentException: Specified Visual is already a child of another Visual or the root of a CompositionTarget" when printing
private IDictionary<int, DocumentPage> _PageCache = new Dictionary<int, DocumentPage>();
public DocumentPaginatorWrapper(DocumentPaginator paginator, Thickness margin)
{
_Margin = margin;
_Paginator = paginator;
this.PageSize = paginator.PageSize;
// Events
paginator.ComputePageCountCompleted += (s, ev) => this.OnComputePageCountCompleted(ev);
paginator.GetPageCompleted += (s, ev) => this.OnGetPageCompleted(ev);
paginator.PagesChanged += (s, ev) => this.OnPagesChanged(ev);
}
public override DocumentPage GetPage(int pageNumber)
{
DocumentPage cachedPage;
if (_PageCache.TryGetValue(pageNumber, out cachedPage) && cachedPage.Visual != null) return cachedPage;
DocumentPage page = _Paginator.GetPage(pageNumber);
// Create a wrapper visual for transformation and add extras
ContainerVisual newpage = new ContainerVisual();
// TODO Transform the wrapped page, add your headers and footers.
// This is highly idiosyncratic.
...
// NB We assume that page.BleedBox has X=0, Y=0, Size=page.PageSize
cachedPage = new DocumentPageWrapper(page, newpage, PageSize, new Rect(PageSize), new Rect(page.ContentBox.X, page.ContentBox.Y, page.ContentBox.Width + _Margin.Left + _Margin.Right, page.ContentBox.Height + _Margin.Top + _Margin.Bottom));
_PageCache[pageNumber] = cachedPage;
return cachedPage;
}
public override bool IsPageCountValid { get { return _Paginator.IsPageCountValid; } }
public override int PageCount { get { return _Paginator.PageCount; } }
public override Size PageSize
{
get { var value = _Paginator.PageSize; return new Size(value.Width + _Margin.Left + _Margin.Right, value.Height + _Margin.Top + _Margin.Bottom); }
set { _Paginator.PageSize = new Size(value.Width - _Margin.Left - _Margin.Right, value.Height - _Margin.Top - _Margin.Bottom); }
}
public override IDocumentPaginatorSource Source { get { return _Paginator.Source; } }
}
/// <summary>
/// It's necessary to dispose the page returned by the wrapped DocumentPaginator when the page we return is disposed,
/// because otherwise we inconsistently get ArgumentExceptions due to the page.Visual still being in use.
/// </summary>
class DocumentPageWrapper : DocumentPage
{
internal DocumentPageWrapper(DocumentPage originalPage, Visual visual, Size pageSize, Rect bleedBox, Rect contentBox)
: base(visual, pageSize, bleedBox, contentBox)
{
_OriginalPage = originalPage;
}
private DocumentPage _OriginalPage;
public override void Dispose()
{
base.Dispose();
if (_OriginalPage != null)
{
_OriginalPage.Dispose();
_OriginalPage = null;
}
}
}
答案 1 :(得分:0)
你可以尝试这个..希望这会有所帮助..
<Section Name="MainBody" SectionExtensions.ResetPageNumbers="True">
<!-- Add the document title to the header -->
<SectionExtensions.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0,0,0,1">
<TextBlock Text="Document title" FontSize="16" />
</Border>
</DataTemplate>
</SectionExtensions.HeaderTemplate>
<!-- Add the page count to the footer -->
<SectionExtensions.FooterTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0,1,0,0">
<TextBlock Text="{Binding}" FontSize="16" />
</Border>
</DataTemplate>
</SectionExtensions.FooterTemplate>
<Paragraph>
...
</Paragraph>
</Section>