【问题标题】:Missing images in FlowDocument saved as XPS document保存为 XPS 文档的 FlowDocument 中缺少图像
【发布时间】:2010-03-11 13:19:12
【问题描述】:

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

这是我的工作:

  1. 使用 WPF 的Image 控件创建图像。我设置了通过调用 BeginInit/EndInit 括起来的图像源。
  2. 将图像添加到 FlowDocument 并将其包装在 BlockUIContainer 中。
  3. 使用 this code 的修改版本将 FlowDocument 对象保存到 XPS 文件中。

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

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

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

我的问题是:

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

【问题讨论】:

  • 您是否找到在打印前在查看器中显示 FlowDocument 的方法?我正在考虑使用类似的“hack”来让我的文档正确呈现。
  • @DennisRoche:不,不幸的是,我从来没有找到比在将文档保存到文件之前在屏幕上短暂显示文档更好的解决方案。如果您找到更好的解决方案,请告诉我。
  • 我可能有一种可能的解决方案,它使用ContextualLayoutManager 走到逻辑树。我会让你知道它是否有效我会让你知道。否则,我将像您一样将文档加载到查看器中,但是会将窗口位置设置为 X:10,000 Y:10,000,以便用户看不到它。

标签: wpf image flowdocument xpsdocument


【解决方案1】:

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

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)中很常见。

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

HTH,

【讨论】:

  • 感谢您的回答。尽管我希望看到不同的解决方案,但我已将您的答案标记为已接受的答案 ;-)。
  • 我希望有一个更好的解决方案,但我相信这已经耗尽了所有其他可能性。这是一个相当干净的解决方法,因为用户永远不会看到窗口出现在屏幕外。
【解决方案2】:

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

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

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

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

【讨论】:

  • 使用 Dispatcher 技巧确实适用于小图像或已经缓存的图像,但不适用于在后台从 Internet 下载的大图像。知道如何延迟保存到 XPS 的尝试,直到 1-2 秒下载完成?或者更好的是,等到 FlowDocument 可能包含的所有图像都完全下载并呈现?
  • @BCA 你可以试试 DispatcherPriority.Idle?
  • 是的,我使用了DispatcherPriority.SystemIdle。如果图像在后台下载,我想调度程序的技巧不起作用?
  • @BCA 听起来很像。您必须跟踪正在下载的图像数量,然后在下载完成后继续。
  • 我想做的是 1)await 首先使用等待/异步下载每个图像(如果它还没有在 WPF 图像缓存中),以及 2)把下载的图像在缓存中,然后 3) 加载我的 FlowDocument。理论上,如果所有图像都已经在缓存中,那么它不会有问题。你知道如何完成#1 和 2 吗?
【解决方案3】:

您不必为了将图像保存到 xps 中而显示文档。你是在 XpsSerializationManager 上调用 commit 吗?

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();
            }
        }

【讨论】:

  • 我尝试调用 XpsSerializationManager.Commit 但没有达到预期的效果。
  • 很抱歉没有尽快回复。这是一个真正的脏版本,应该可以工作。让我知道结果如何
  • 我还没有机会尝试。我会回复你的:-)
  • 您有机会尝试一下吗?
【解决方案4】:

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

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)));

【讨论】:

    猜你喜欢
    • 2011-02-17
    • 2014-03-18
    • 1970-01-01
    • 2023-03-19
    • 2012-05-03
    • 1970-01-01
    • 2016-12-07
    • 1970-01-01
    相关资源
    最近更新 更多