You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过FixedDoc将WPF控件转为XPS文档?解决子元素关联错误

WPF导出横向XPS时控件已被占用的错误解决

问题场景

我用XAML制作测试报告,需要导出为XPS文件:

  • 最初用这段代码能成功导出,但结果是纵向布局,不知道怎么改成横向:
XpsDocument xpsDoc = new XpsDocument(pathFileNameXPS, FileAccess.ReadWrite);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
xpsWriter.Write(TestReportXAML);
xpsDoc.Close();
  • 尝试用FixedDocument实现横向导出时,执行fixedPage.Children.Add(TestReportXAML)报错:

Specified element is already the logical child of another element. Disconnect it first.

其中TestReportXAML是XAML对应的Grid控件,报错的完整代码如下:

FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = new FixedPage();

fixedDoc.DocumentPaginator.PageSize = new Size(96 * 11, 96 * 8.5); // LANDSCAPE
fixedPage.Width = 11 * 96;
fixedPage.Height = 8.5 * 96;

fixedPage.Children.Add(TestReportXAML);
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
fixedDoc.Pages.Add(pageContent);

XpsDocument xpsDoc = new XpsDocument(pathFileNameXPS, FileAccess.ReadWrite);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
xpsWriter.Write(fixedDoc);
xpsDoc.Close();

错误原因

WPF的UI控件(比如你的Grid)只能属于一个父容器。TestReportXAML已经是你界面上某个容器的子控件了,直接把它加到FixedPage里,就会因为“一个控件不能有两个父容器”而报错。

解决方案

方案1:先移除原父容器,再添加到FixedPage

如果导出后不需要保留界面上的TestReportXAML,或者可以事后恢复,就先把它从原来的父容器里拆出来:

// 先把TestReportXAML从原父容器移除
if (TestReportXAML.Parent is Panel parentPanel)
{
    parentPanel.Children.Remove(TestReportXAML);
}

// 后续执行原来的FixedDocument导出代码
FixedDocument fixedDoc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage fixedPage = new FixedPage();

fixedDoc.DocumentPaginator.PageSize = new Size(96 * 11, 96 * 8.5);
fixedPage.Width = 11 * 96;
fixedPage.Height = 8.5 * 96;

fixedPage.Children.Add(TestReportXAML);
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
fixedDoc.Pages.Add(pageContent);

XpsDocument xpsDoc = new XpsDocument(pathFileNameXPS, FileAccess.ReadWrite);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
xpsWriter.Write(fixedDoc);
xpsDoc.Close();

// 如果需要把控件放回原界面,这里再加回去
// parentPanel.Children.Add(TestReportXAML);

方案2:克隆控件副本导出(推荐)

不想改动原界面的话,最好的办法是克隆一份TestReportXAML的副本,用副本做导出,这样原控件不受影响:

// 序列化克隆控件副本
string xamlString = System.Windows.Markup.XamlWriter.Save(TestReportXAML);
using (StringReader stringReader = new StringReader(xamlString))
{
    using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(stringReader))
    {
        Grid clonedReport = (Grid)System.Windows.Markup.XamlReader.Load(xmlReader);

        // 用副本执行横向导出
        FixedDocument fixedDoc = new FixedDocument();
        PageContent pageContent = new PageContent();
        FixedPage fixedPage = new FixedPage();

        fixedDoc.DocumentPaginator.PageSize = new Size(96 * 11, 96 * 8.5);
        fixedPage.Width = 11 * 96;
        fixedPage.Height = 8.5 * 96;

        fixedPage.Children.Add(clonedReport);
        ((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
        fixedDoc.Pages.Add(pageContent);

        XpsDocument xpsDoc = new XpsDocument(pathFileNameXPS, FileAccess.ReadWrite);
        XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
        xpsWriter.Write(fixedDoc);
        xpsDoc.Close();
    }
}

补充说明

最初的代码能直接导出纵向,是因为WPF自动帮你处理了容器和分页,但它默认使用纵向页面尺寸,没法直接修改,所以必须用FixedDocument手动指定横向的宽高(96是WPF默认的DPI,11英寸宽×8.5英寸高就是横向的标准纸张尺寸)。

内容的提问来源于stack exchange,提问作者Sqwirl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:30:52