Отсутствующие изображения в FlowDocument сохранены как документ XPS - PullRequest
7 голосов
/ 11 марта 2010

У меня возникают трудности с получением изображений, содержащихся в FlowDocument, для отображения при сохранении FlowDocument в виде документа XPS.

Вот что я делаю:

  1. Создайте изображение с помощью Изображение элемента управления WPF. Я установил источник изображения в скобках при вызовах BeginInit / EndInit.
  2. Добавьте изображение в FlowDocument, поместив его в BlockUIContainer .
  3. Сохраните объект FlowDocument в файл XPS, используя модифицированную версию этот код .

Если я затем просматриваю сохраненный файл в средстве просмотра XPS, изображение не отображается. Проблема заключается в том, что изображения не загружаются до тех пор, пока WPF не отобразится на экране, поэтому они не сохраняются в файл XPS. Следовательно, есть обходной путь: если я сначала покажу документ на экране, используя FlowDocumentPageViewer , а затем сохраню файл XPS, изображение будет загружено и отобразится в файле XPS. Это работает, даже если FlowDocumentPageViewer скрыт. Но это дает мне еще одну проблему. Вот что я хочу сделать (в псевдокоде):

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

Это, конечно, не работает, так как FlowDocumentPageViewer никогда не получает возможности показать свое содержимое до сохранения документа в файл XPS. Я попытался обернуть SaveFlowDocumentToXpsFile в вызов Dispatcher.BeginInvoke, но это не помогло.

Мои вопросы:

  1. Можно ли как-то принудительно загрузить изображения перед сохранением файла XPS, не отображая документ на экране? (Я попробовал возиться с BitmapImage.CreateOptions , но безуспешно).
  2. Если нет решения для вопроса № 1, есть ли способ узнать, когда FlowDocumentPageViewer завершил загрузку своего содержимого, чтобы я знал, когда он сохраняется для создания файла XPS?

Ответы [ 3 ]

1 голос
/ 27 февраля 2012

Возможное решение было таким же, как вы пришли, чтобы поместить документ в средство просмотра и кратко показать его на экране. Ниже приведен вспомогательный метод, который я написал, чтобы сделать это для меня.

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,

1 голос
/ 18 января 2011

Пара вещей ... Вы уверены, что размер изображения до его написания? Обычно вы должны вызывать Measure для элемента управления, чтобы он мог соответствующим образом изменить размер (бесконечность позволяет элементу управления расширяться до его ширины и высоты)

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

Кроме того, иногда вам приходится увеличивать поток пользовательского интерфейса, чтобы все обновлялось в элементе управления

Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>{}));
0 голосов
/ 12 марта 2012

Вам не нужно отображать документ, чтобы сохранить изображения в xps. Вы вызываете commit на 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();
            }
        }
...