如何通过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
相关产品推荐
相关产品推荐

