gpt4 book ai didi

c# - 呈现为 XPS 时如何创建带有页眉和页脚的 Xaml FlowDocument?

转载 作者:可可西里 更新时间:2023-11-01 09:08:24 26 4
gpt4 key购买 nike

我正在寻找一种干净的通用方法来描述 XAML FlowDocument 中重复的页眉和页脚,而无需任何代码。它只需要在从 C# 呈现到 XPS 时正确显示。

最佳答案

几个月前我遇到了同样的问题,发现这些链接非常有用: WPF Multipage Reports Part IV Pagination http://www.codeproject.com/KB/WPF/PimpedDocumentPaginator.aspx

我使用的基本技术是通过从 DynamicDocumentPaginator 派生来创建自定义分页器,如下所示:

internal class HeaderFooterPaginator<THeaderModel, TFooterModel> : DynamicDocumentPaginator where THeaderModel : PageNumberModel, new() where TFooterModel : PageNumberModel, new()
{
...
}

在我的例子中,THeaderFooterModelTFooterModelPageNumberModel 类型的子类,因为我需要页眉或页脚才能显示当前页码。

public class PageNumberModel
{
public int PageNumber { get; set; }
}

自定义分页器将大部分工作委托(delegate)给原始 XPS 分页器,因此它将其存储在构造函数中。

THeaderModelTFooterModel 类型允许分页器检索每种类型的 XAML DataTemplates,这允许您指定布局XAML 中的页眉和页脚,而无需诉诸自定义绘图代码。

在我的代码中,页眉和页脚的大小是固定的,因此在创建分页器时,它会检索页眉和页脚模板以确定要保留多少空间。

在提供的链接中的示例代码中,他们用于为页眉和页脚保留空间的技术是使用缩放变换来缩小原始内容。相反,我告诉原始分页器使用缩小的页面大小,然后将原始分页器生成的页面添加到 ContainerVisual 并设置其 Offset。如果页眉和页脚的大小是动态的,您可能无法这样做,因为页数会不断变化。

我记得的唯一其他复杂情况是您在添加页眉和页脚时需要使用 Dispatcher 队列(请参阅下面的 AddHeaderOrFooterToContainerAsync)。否则数据绑定(bind)不起作用。我们稍微颠覆了 WPF 渲染模型以使其正常工作。

如果不包括代码,这一切都很难解释,所以我在下面附上了自定义渲染器代码。我已经删除了一些不相关的东西,所以如果它不能按原样编译,这可能就是原因:-)

请注意,页码偏移量已传入,因为我们的 XPS 文档由多个 FlowDocument 部分组成,并且调用代码会跟踪当前的总页码。

希望这对您有所帮助!

internal class HeaderFooterPaginator<THeaderModel, TFooterModel> : DynamicDocumentPaginator where THeaderModel : PageNumberModel, new() where TFooterModel : PageNumberModel, new()
{
private readonly double _footerHeight;
private readonly DataTemplate _footerTemplate;
private readonly double _headerHeight;
private readonly DataTemplate _headerTemplate;
private Size _newPageSize;
private Size _originalPageSize;
private readonly DynamicDocumentPaginator _originalPaginator;
private readonly int _pageNumberOffset;
private readonly FlowDocument _paginatorSource;
private const double HeaderAndFooterMarginHeight = 5;
private const double HeaderAndFooterMarginWidth = 10;

public HeaderFooterPaginator(int pageNumberOffset, FlowDocument document)
{
if (document == null)
{
throw new ArgumentNullException("document");
}

_paginatorSource = document;

if (_paginatorSource == null)
{
throw new InvalidOperationException("Could not create a clone of the document being paginated.");
}

_originalPaginator = ((IDocumentPaginatorSource) _paginatorSource).DocumentPaginator as DynamicDocumentPaginator;

if (_originalPaginator == null)
{
throw new InvalidOperationException("The paginator must be a DynamicDocumentPaginator.");
}

_headerTemplate = GetModelDataTemplate(typeof (THeaderModel));
_footerTemplate = GetModelDataTemplate(typeof (TFooterModel));

var headerSize = GetModelContentSize(new THeaderModel { PageNumber = int.MaxValue }, _headerTemplate, _originalPaginator.PageSize);
var footerSize = GetModelContentSize(new TFooterModel { PageNumber = int.MaxValue }, _footerTemplate, _originalPaginator.PageSize);

_headerHeight = double.IsInfinity(headerSize.Height) ? 0 : headerSize.Height;
_footerHeight = double.IsInfinity(footerSize.Height) ? 0 : footerSize.Height;

_pageNumberOffset = pageNumberOffset;

SetPageSize(new Size(document.PageWidth, document.PageHeight));
}

private void AddHeaderOrFooterToContainerAsync<THeaderOrFooter>(ContainerVisual container, double areaWidth, double areaHeight, Vector areaOffset, FrameworkTemplate template, int displayPageNumber) where THeaderOrFooter : PageNumberModel, new()
{
if (template == null)
{
return;
}
var visual = GetModelContent(new THeaderOrFooter { PageNumber = displayPageNumber }, template);

if (visual != null)
{
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
visual.Measure(_originalPageSize);
visual.Arrange(new Rect(0, 0, areaWidth, areaHeight));
visual.UpdateLayout();

var headerContainer = new ContainerVisual { Offset = areaOffset };
headerContainer.Children.Add(visual);
container.Children.Add(headerContainer);
}));
}
}

public override void ComputePageCount()
{
_originalPaginator.ComputePageCount();
}

private static void FlushDispatcher()
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.ApplicationIdle, new DispatcherOperationCallback(delegate { return null; }), null);
}

private static FrameworkElement GetModelContent(object dataModel, FrameworkTemplate modelTemplate)
{
if (modelTemplate == null)
{
return null;
}

var content = modelTemplate.LoadContent() as FrameworkElement;
if (content == null)
{
return null;
}

content.DataContext = dataModel;

return content;
}

private static Size GetModelContentSize(object dataModel, FrameworkTemplate modelTemplate, Size availableSize)
{
var content = GetModelContent(dataModel, modelTemplate);
if (content == null)
{
return Size.Empty;
}

FlushDispatcher();
content.Measure(availableSize);
return content.DesiredSize;
}

private DataTemplate GetModelDataTemplate(Type modelType)
{
var key = new DataTemplateKey(modelType);
return _paginatorSource.TryFindResource(key) as DataTemplate;
}

public override ContentPosition GetObjectPosition(object value)
{
return _originalPaginator.GetObjectPosition(value);
}

public override DocumentPage GetPage(int pageNumber)
{
if (!_originalPaginator.IsPageCountValid)
{
ComputePageCount();
}

var originalPage = _originalPaginator.GetPage(pageNumber);

var newPage = new ContainerVisual();

var displayPageNumber = _pageNumberOffset + pageNumber;
var internalWidth = _originalPageSize.Width - 2*HeaderAndFooterMarginWidth;
AddHeaderOrFooterToContainerAsync<THeaderModel>(newPage, internalWidth, _headerHeight, new Vector(HeaderAndFooterMarginWidth, HeaderAndFooterMarginHeight), _headerTemplate, displayPageNumber);

var smallerPage = new ContainerVisual();
smallerPage.Children.Add(originalPage.Visual);
smallerPage.Offset = new Vector(HeaderAndFooterMarginWidth, HeaderAndFooterMarginHeight + _headerHeight);
newPage.Children.Add(smallerPage);

AddHeaderOrFooterToContainerAsync<TFooterModel>(newPage, internalWidth, _footerHeight, new Vector(HeaderAndFooterMarginWidth, _originalPageSize.Height - HeaderAndFooterMarginHeight - _footerHeight), _footerTemplate, displayPageNumber);

return new DocumentPage(newPage, _originalPageSize, originalPage.BleedBox, originalPage.ContentBox);
}

public override int GetPageNumber(ContentPosition contentPosition)
{
return _originalPaginator.GetPageNumber(contentPosition);
}

public override ContentPosition GetPagePosition(DocumentPage page)
{
return _originalPaginator.GetPagePosition(page);
}

private void SetPageSize(Size pageSize)
{
_originalPageSize = pageSize;

// Decrease the available page size by the height of the header and footer. The page is offset by the header height later on.
var sizeRect = new Rect(pageSize);
sizeRect.Inflate(-HeaderAndFooterMarginWidth, -(HeaderAndFooterMarginHeight + _footerHeight/2 + _headerHeight/2));

_originalPaginator.PageSize = _newPageSize = sizeRect.Size;

// Change page size of the document to the size of the content area
_paginatorSource.PageHeight = _newPageSize.Height;
_paginatorSource.PageWidth = _newPageSize.Width;
}

public override bool IsPageCountValid
{
get { return _originalPaginator.IsPageCountValid; }
}

public override int PageCount
{
get { return _originalPaginator.PageCount; }
}

public override Size PageSize
{
get { return _newPageSize; }
set { SetPageSize(value); }
}

public override IDocumentPaginatorSource Source
{
get { return _originalPaginator.Source; }
}
}

关于c# - 呈现为 XPS 时如何创建带有页眉和页脚的 Xaml FlowDocument?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5172781/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com