另存为 XPS 文档的 FlowDocument 中缺少图像

发布于 2024-08-24 22:24:20 字数 1644 浏览 8 评论 0原文

当 FlowDocument 保存为 XPS 文档时,获取 FlowDocument 中包含的图像以显示时遇到一些困难。

这是我所做的:

  1. 使用 WPF 的图像 控件。我将图像源设置为通过调用 BeginInit/EndInit 括起来。
  2. 将图像添加到 FlowDocument 中,将其包装在 BlockUIContainer。
  3. 使用修改版本的 此代码

如果我随后在 XPS 查看器中查看保存的文件,则不会显示图像。问题是图像在 WPF 实际显示在屏幕上之前不会加载,因此不会保存到 XPS 文件中。因此,有一个解决方法:如果我首先使用 FlowDocumentPageViewer 然后保存 XPS 文件,图像将被加载并显示在 XPS 文件中。即使 FlowDocumentPageViewer 处于隐藏状态,此操作也有效。但这给了我另一个挑战。这是我想要做的(以伪代码):

void SaveDocument()
{
    AddFlowDocumentToFlowDocumentPageViewer();
    SaveFlowDocumentToXpsFile();
}

这当然行不通,因为在将文档保存到 XPS 文件之前,FlowDocumentPageViewer 永远没有机会显示其内容。我尝试将 SaveFlowDocumentToXpsFile 包装在对 Dispatcher.BeginInvoke 的调用中,但没有帮助。

我的问题是:

  1. 我是否可以在保存 XPS 文件之前强制加载图像,而不在屏幕上实际显示文档? (我尝试摆弄 BitmapImage.CreateOptions 没有运气)。
  2. 如果问题#1 没有解决方案,有没有办法告诉 FlowDocumentPageViewer 何时完成加载其内容,以便我知道何时保存以创建 XPS 文件?

I am having some difficulties getting images contained in a FlowDocument to show when the FlowDocument is saved as an XPS document.

Here is what I do:

  1. Create an image using the Image control of WPF. I set the image source bracketed by calls to BeginInit/EndInit.
  2. Add the image to the FlowDocument wrapping it in a BlockUIContainer.
  3. Save the FlowDocument object to an XPS file using a modified version of this code.

If I then view the saved file in the XPS viewer, the image is not shown. The problem is that the images are not loaded until actually shown on the screen by WPF so they are not saved to the XPS file. Hence, there is a workaround: If I first show the document on screen using the FlowDocumentPageViewer and then save the XPS file afterwards, the image is loaded and shows up in the XPS file. This works even if the FlowDocumentPageViewer is hidden. But that gives me another challenge. Here is what I wish to do (in pseudocode):

void SaveDocument()
{
    AddFlowDocumentToFlowDocumentPageViewer();
    SaveFlowDocumentToXpsFile();
}

This of course does not work since the FlowDocumentPageViewer never gets a chance to show its contents before the document is saved to the XPS file. I tried wrapping SaveFlowDocumentToXpsFile in a call to Dispatcher.BeginInvoke but it did not help.

My questions are:

  1. Can I somehow force the images to load before saving the XPS file without actually showing the document on screen? (I tried fiddling with BitmapImage.CreateOptions with no luck).
  2. If there is no solution to question #1, is there a way to tell when FlowDocumentPageViewer has finished loading its contents so that I know when it is save to create the XPS file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

¢好甜 2024-08-31 22:24:20

最终的解决方案与您遇到的相同,即将文档放入查看器中并在屏幕上短暂显示。下面是我为我编写的辅助方法。

private static string ForceRenderFlowDocumentXaml = 
@"<Window xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
          xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
       <FlowDocumentScrollViewer Name=""viewer""/>
  </Window>";

public static void ForceRenderFlowDocument(FlowDocument document)
{
    using (var reader = new XmlTextReader(new StringReader(ForceRenderFlowDocumentXaml)))
    {
        Window window = XamlReader.Load(reader) as Window;
        FlowDocumentScrollViewer viewer = LogicalTreeHelper.FindLogicalNode(window, "viewer") as FlowDocumentScrollViewer;
        viewer.Document = document;
        // Show the window way off-screen
        window.WindowStartupLocation = WindowStartupLocation.Manual;
        window.Top = Int32.MaxValue;
        window.Left = Int32.MaxValue;
        window.ShowInTaskbar = false;
        window.Show();
        // Ensure that dispatcher has done the layout and render passes
        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {}));
        viewer.Document = null;
        window.Close();
    }
}

编辑:我刚刚将window.ShowInTaskbar = false添加到该方法中,就好像您很快就可以看到该窗口出现在任务栏中一样。

用户永远不会“看到”该窗口,因为它位于屏幕外的 Int32.MaxValue 位置 - 这是早期多媒体创作(例如 Macromedia/Adobe Director)中常见的技巧。

对于搜索和找到这个问题的人,我可以告诉您没有其他方法来强制文档呈现。

哈特哈,

The eventual solution was the same as you came to, which is to put the document in a viewer and briefly show it on screen. Below is the helper method that I wrote to do this for me.

private static string ForceRenderFlowDocumentXaml = 
@"<Window xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
          xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
       <FlowDocumentScrollViewer Name=""viewer""/>
  </Window>";

public static void ForceRenderFlowDocument(FlowDocument document)
{
    using (var reader = new XmlTextReader(new StringReader(ForceRenderFlowDocumentXaml)))
    {
        Window window = XamlReader.Load(reader) as Window;
        FlowDocumentScrollViewer viewer = LogicalTreeHelper.FindLogicalNode(window, "viewer") as FlowDocumentScrollViewer;
        viewer.Document = document;
        // Show the window way off-screen
        window.WindowStartupLocation = WindowStartupLocation.Manual;
        window.Top = Int32.MaxValue;
        window.Left = Int32.MaxValue;
        window.ShowInTaskbar = false;
        window.Show();
        // Ensure that dispatcher has done the layout and render passes
        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {}));
        viewer.Document = null;
        window.Close();
    }
}

Edit: I just added window.ShowInTaskbar = false to the method as if you were quick you could see the window appear in the taskbar.

The user will never "see" the window as it is positioned way off-screen at Int32.MaxValue - a trick that was common back in the day with early multimedia authoring (e.g. Macromedia/Adobe Director).

For people searching and finding this question, I can tell you that there is no other way to force the document to render.

HTH,

梦断已成空 2024-08-31 22:24:20

几件事...
您确定图像在写入之前已调整大小吗?通常,您必须在控件上调用 Measure,以便它可以相应地调整自身大小(无穷大让控件扩展到其宽度和高度)

image.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

此外,有时您必须碰撞 UI 线程,以便控件中的所有内容都得到更新

Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>{}));

Couple things...
You sure the image is sized before its written? Usually you have to call Measure on the control so that it may size itself accordingly (infinity lets the control expand to its Width and Height)

image.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

Also, sometimes you have to bump the UI thread so that everything gets updated in the control

Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>{}));
水染的天色ゝ 2024-08-31 22:24:20

您不必显示文档即可将图像保存到 xps 中。您是否在 XpsSerializationManager 上调用提交?

FlowDocument fd = new FlowDocument();

        fd.Blocks.Add(new Paragraph(new Run("This is a test")));

        string image = @"STRING_PATH";

        BitmapImage bi = new BitmapImage();
        bi.BeginInit();
        bi.UriSource = new Uri(image, UriKind.RelativeOrAbsolute);
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.EndInit();
        MemoryStream ms = new MemoryStream();
        Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
        Uri pkgUri = bi.UriSource;

        PackageStore.AddPackage(pkgUri, pkg);


        Image img = new Image();
        img.Source = bi;

        BlockUIContainer blkContainer = new BlockUIContainer(img);

        fd.Blocks.Add(blkContainer);


        DocumentPaginator paginator = ((IDocumentPaginatorSource)fd).DocumentPaginator;

      using (XpsDocument xps = new XpsDocument(@"STRING PATH WHERE TO SAVE FILE", FileAccess.ReadWrite, CompressionOption.Maximum))
        {
            using (XpsSerializationManager serializer = new XpsSerializationManager(new XpsPackagingPolicy(xps), false))
            {
                serializer.SaveAsXaml(paginator);
                serializer.Commit();
            }
        }

You do not have to display the document in order to have images saved into the xps. Are you calling commit on the XpsSerializationManager?

FlowDocument fd = new FlowDocument();

        fd.Blocks.Add(new Paragraph(new Run("This is a test")));

        string image = @"STRING_PATH";

        BitmapImage bi = new BitmapImage();
        bi.BeginInit();
        bi.UriSource = new Uri(image, UriKind.RelativeOrAbsolute);
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.EndInit();
        MemoryStream ms = new MemoryStream();
        Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
        Uri pkgUri = bi.UriSource;

        PackageStore.AddPackage(pkgUri, pkg);


        Image img = new Image();
        img.Source = bi;

        BlockUIContainer blkContainer = new BlockUIContainer(img);

        fd.Blocks.Add(blkContainer);


        DocumentPaginator paginator = ((IDocumentPaginatorSource)fd).DocumentPaginator;

      using (XpsDocument xps = new XpsDocument(@"STRING PATH WHERE TO SAVE FILE", FileAccess.ReadWrite, CompressionOption.Maximum))
        {
            using (XpsSerializationManager serializer = new XpsSerializationManager(new XpsPackagingPolicy(xps), false))
            {
                serializer.SaveAsXaml(paginator);
                serializer.Commit();
            }
        }
冰雪之触 2024-08-31 22:24:20

我能够通过将流程文档放入查看器中来解决这个问题,然后进行测量/安排。

FlowDocumentScrollViewer flowDocumentScrollViewer = new FlowDocumentScrollViewer();
flowDocumentScrollViewer.Document = flowDocument;
flowDocumentScrollViewer.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
flowDocumentScrollViewer.Arrange(new Rect(new Point(0, 0), new Point(Double.MaxValue, Double.MaxValue)));

I was able to address this by throwing the flowdocument into a viewer, and then do a measure/arrange.

FlowDocumentScrollViewer flowDocumentScrollViewer = new FlowDocumentScrollViewer();
flowDocumentScrollViewer.Document = flowDocument;
flowDocumentScrollViewer.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
flowDocumentScrollViewer.Arrange(new Rect(new Point(0, 0), new Point(Double.MaxValue, Double.MaxValue)));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文