印刷BlockUIContainer到XpsDocument /固定文档 [英] Printing BlockUIContainer to XpsDocument/FixedDocument

查看:515
本文介绍了印刷BlockUIContainer到XpsDocument /固定文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述




  1. 如何打印具有的FlowDocument BlockUIContainer

  2. 如何强制措施/更新/安排上的FlowDocument?



背景



我有一个生成的的FlowDocument 与段落文字几个长方形填充元素 DrawingBrushes 从资源字典和 BlockUIContainer 自定义控制。



该文件中的任何的FlowDocument *控制的查看时正确呈现无论其当文档转换为固定文档/ XpsDocument,没有的长方形 BlockUIContainer 元素的渲染。



我几乎可以肯定这是因为控制有没有被测量/安排,但无法弄清楚如何强制,要发生之前它被转换为XpsDocument。




  • 我已经走了 LogicalTree 递归,做以下,

     的UIElement元素=(的UIElement)D; 
    element.Measure(新尺寸(Double.PositiveInfinity,Double.PositiveInfinity));
    element.Arrange(新矩形(element.DesiredSize));
    element.UpdateLayout();



    其中, D 的DependencyObject 。我可以看到,这台 ActualWidth的的ActualHeight 属性中的调试器时破裂指出。


  • 我试图迫使调度渲染通过的Will♦




代码用于打印XpsDocument

 公共类XpsDocumentConverter 
{

公静态XpsDocumentReference CreateXpsDocument(FlowDocument的文档)
{
//需要克隆文档,以便在分页程序可以工作
的FlowDocument clonedDocument = DocumentHelper.Clone<&FlowDocument的GT;(文件);

乌里URI =新的URI(的String.Format(包:// temp_ {0} .XPS /,Guid.NewGuid()的ToString(N)));
的MemoryStream毫秒​​=新的MemoryStream();

包pkg = Package.Open(MS,FileMode.Create,FileAccess.ReadWrite);
PackageStore.AddPackage(URI,PKG);
XpsDocument xpsDocument =新XpsDocument(PKG,CompressionOption.Normal,uri.AbsoluteUri);

XpsSerializationManager RS​​M =新XpsSerializationManager(新XpsPackagingPolicy(xpsDocument),FALSE);
DocumentPaginator分页程序=新FixedDocumentPaginator(clonedDocument,A4PageDefinition.Default);
rsm.SaveAsXaml(分页程序);

返回新XpsDocumentReference(MS,xpsDocument);
}

}



正如你可以看到我还使用自定义 DocumentPaginator 名为FixedDocumentPaginator';然而,我怀疑这个问题是存在的由它开始于 GETPAGE分页文件的时候,我不会发表码(INT PAGENUMBER)一切都已经转换一个视觉,这是为时已晚布局。






修改



嗯。由于我打字这一点,以为只是发生,我认为在克隆文档可能不会有一个测量/排列/ UpdateLayout请完成。



问:如何强制措施/更新上的FlowDocument /安排



一个可能的黑客,我的工作可能会显示克隆文档中的FlowDocumentViewers之一(也许关闭屏幕)。



这是我刚刚了解并没有试图将调用另一个可能的解决方案: ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout( );



ContextLayoutManager 遍历逻辑树为你和更新的布局<。 / p>

代码用于克隆文件

 公共静态的FlowDocument克隆(FlowDocument的originalDocument)
{
的FlowDocument clonedDocument =新的FlowDocument();
的TextRange sourceDocument =新的TextRange(originalDocument.ContentStart,originalDocument.ContentEnd);
的TextRange clonedDocumentRange =新的TextRange(clonedDocument.ContentStart,clonedDocument.ContentEnd);

{使用
(MemoryStream的毫秒=新的MemoryStream())
{
sourceDocument.Save(MS,DataFormats.XamlPackage);
clonedDocumentRange.Load(MS,DataFormats.XamlPackage);
}

clonedDocument.ColumnWidth = originalDocument.ColumnWidth;
clonedDocument.PageWidth = originalDocument.PageWidth;
clonedDocument.PageHeight = originalDocument.PageHeight;
clonedDocument.PagePadding = originalDocument.PagePadding;
clonedDocument.LineStackingStrategy = clonedDocument.LineStackingStrategy;

返回clonedDocument;
}
赶上(例外)
{
}

返回NULL;
}


解决方案

发布此作为未来的参考。其他人都具有的FlowDocument /固定文档/ XpsDocument类似的渲染问题。



有几件事情需要注意:




  • BlockUIContainers 当你使用上面的方法不会克隆。这不是显而易见的,直到我用一些辅助的方法(这些方法都贴在下面 - 他们是非常有用的)打印的逻辑树了调试窗口。

  • 您需要在浏览器中显示的文档,并简要显示在屏幕上。下面是我写的,我做的helper方法。



ForceRenderFlowDocument



 私有静态字符串ForceRenderFlowDocumentXaml = 
@<窗​​口的xmlns =http://schemas.microsoft.com/netfx/2007/xaml/presentation
的xmlns:X = http://schemas.microsoft.com/winfx/2006/xaml\"\">
< FlowDocumentScrollViewer名称=浏览器/>
< /窗gt;中;

公共静态无效ForceRenderFlowDocument(FlowDocument的文档)
{
使用(VAR读卡器=新的XmlTextReader的(新StringReader(ForceRenderFlowDocumentXaml)))
{
窗口窗口= XamlReader.Load(阅读器)的窗口;
FlowDocumentScrollViewer观众= LogicalTreeHelper.FindLogicalNode(窗口,在观众)作为FlowDocumentScrollViewer;
viewer.Document =文件;
//显示窗口的方式关闭屏幕
window.WindowStartupLocation = WindowStartupLocation.Manual;
window.Top = Int32.MaxValue;
window.Left = Int32.MaxValue;
window.ShowInTaskbar = FALSE;
window.Show();
//确保调度做了布局和渲染过程
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Loaded,新的行动(()=> {}));
viewer.Document = NULL;
window.close()的;
}
}



编辑:我刚加入 window.ShowInTaskbar = FALSE 的方法,如果你是快,你可以​​看到窗口出现在任务栏中。



用户永远不会看,因为它是在 Int32.MaxValue 定位方式关闭屏幕的窗口 - 一招,这是常见早在早期多媒体创作(如Macromedia公司/使用Adobe董事)的日子。



有关人民搜索和发现这个问题,我可以告诉你,< STRONG>有没有别的办法以强制文件进行渲染。



视觉与逻辑树助手



 公共静态字符串WriteVisualTree(DependencyObject的母公司)
{
如果(家长== NULL)
返回不可视树可用的DependencyObject为空。

使用(VAR的StringWriter =新的StringWriter()),使用(VAR indentedTextWriter =新IndentedTextWriter(StringWriter的,))
{
WriteVisualTreeRecursive
(indentedTextWriter,父,0);
返回stringWriter.ToString();
}
}

私有静态无效WriteVisualTreeRecursive(IndentedTextWriter作家的DependencyObject父母,INT indentLevel)
{
如果(家长== NULL)
回报;

INT childCount = VisualTreeHelper.GetChildrenCount(父);
字符串的typeName = parent.GetType()名称。
串OBJNAME = parent.GetValue(FrameworkElement.NameProperty)的字符串;

writer.Indent = indentLevel;
writer.WriteLine(的String.Format([{0:000}] {1}({2}){3},indentLevel,
String.IsNullOrEmpty(OBJNAME)的typeName:OBJNAME,
的typeName,childCount)
);

的for(int childIndex = 0; childIndex< childCount ++ childIndex)
WriteVisualTreeRecursive(作家,VisualTreeHelper.GetChild(父母,childIndex),indentLevel + 1);
}

公共静态字符串WriteLogicalTree(DependencyObject的母公司)
{
如果(家长== NULL)
返回没有逻辑树可用。DependencyObject的是空值。;

使用(VAR的StringWriter =新的StringWriter()),使用(VAR indentedTextWriter =新IndentedTextWriter(StringWriter的,))
{
WriteLogicalTreeRecursive
(indentedTextWriter,父,0);
返回stringWriter.ToString();
}
}

私有静态无效WriteLogicalTreeRecursive(IndentedTextWriter作家的DependencyObject父母,INT indentLevel)
{
如果(家长== NULL)
回报;

VAR孩子= LogicalTreeHelper.GetChildren(父).OfType<&的DependencyObject GT;();
INT childCount = children.Count();

字符串的typeName = parent.GetType()名称。
串OBJNAME = parent.GetValue(FrameworkElement.NameProperty)的字符串;

双ActualWidth的=(parent.GetValue(FrameworkElement.ActualWidthProperty)为双?)GetValueOrDefault()。
双的ActualHeight =(parent.GetValue(FrameworkElement.ActualHeightProperty),为双?)GetValueOrDefault();

writer.Indent = indentLevel;
writer.WriteLine(的String.Format([{0:000}] {1}({2}){3},indentLevel,
String.IsNullOrEmpty(OBJNAME)的typeName:OBJNAME,
的typeName,
childCount)
);

的foreach(对象的孩子LogicalTreeHelper.GetChildren(父))
{
如果(孩子的DependencyObject)
WriteLogicalTreeRecursive(作家(DependencyObject的)孩子,indentLevel + 1);
}

}



用法

 #如果DEBUG 
的Debug.WriteLine(---开始-------) ;
的Debug.WriteLine(VisualA​​ndLogicalTreeHelper.WriteLogicalTree(文件));
的Debug.WriteLine(---完-------);
#ENDIF


Question

  1. How do you print a FlowDocument that have BlockUIContainer?
  2. How can I force a Measure/Update/Arrange on a FlowDocument?

Background

I have a generated FlowDocument with paragraphs of text with a few Rectangle elements filled DrawingBrushes from a resource dictionary and BlockUIContainer with custom controls.

The document renders correctly when viewed in any of the FlowDocument* controls HOWEVER when the document is converted to a FixedDocument/XpsDocument, none of the Rectangle or BlockUIContainer elements render.

I'm almost certain it is because the control has not been measured/arranged, however cannot figure out how to force that to happen before it is converted to the XpsDocument.

  • I have walked the LogicalTree recursively and done the following,

    UIElement element = (UIElement)d;
    element.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    element.Arrange(new Rect(element.DesiredSize));
    element.UpdateLayout();
    

    where d is a DependencyObject. I can see that this sets the ActualWidth and ActualHeight properties when break-pointed in the debugger.

  • I have tried forcing the Dispatcher to render as suggested by Will ♦.

Code used to print the XpsDocument

public class XpsDocumentConverter
{

    public static XpsDocumentReference CreateXpsDocument(FlowDocument document)
    {
        // Need to clone the document so that the paginator can work
        FlowDocument clonedDocument = DocumentHelper.Clone<FlowDocument>(document);

        Uri uri = new Uri(String.Format("pack://temp_{0}.xps/", Guid.NewGuid().ToString("N")));
        MemoryStream ms = new MemoryStream();

        Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
        PackageStore.AddPackage(uri, pkg);
        XpsDocument xpsDocument = new XpsDocument(pkg, CompressionOption.Normal, uri.AbsoluteUri);

        XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDocument), false);
        DocumentPaginator paginator = new FixedDocumentPaginator(clonedDocument, A4PageDefinition.Default);
        rsm.SaveAsXaml(paginator);

        return new XpsDocumentReference(ms, xpsDocument);
    }

}

As you can see I'm also using a custom DocumentPaginator named 'FixedDocumentPaginator'; however I will not post that code as I doubt the issue is there as by the time it starts paginating the document in GetPage(int pageNumber) everything has already been converted a Visual and it is too late for layout.


Edit

Hmm. As I typed this, a thought just occurred to me that the cloned document may not have had a Measure/Arrange/UpdateLayout done.

Question: How can I force a Measure/Update/Arrange on a FlowDocument?

A possible hack that I could work would be to show the cloned document in one of the FlowDocumentViewers (perhaps off-screen).

Another possible solution that I just learnt about and haven't tried would be to call: ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout();

ContextLayoutManager walks the logical tree for you and updates the layout.

Code used for cloning the document

public static FlowDocument Clone(FlowDocument originalDocument)
{
    FlowDocument clonedDocument = new FlowDocument();
    TextRange sourceDocument = new TextRange(originalDocument.ContentStart, originalDocument.ContentEnd);
    TextRange clonedDocumentRange = new TextRange(clonedDocument.ContentStart, clonedDocument.ContentEnd);
    try
    {
        using (MemoryStream ms = new MemoryStream())
        {
            sourceDocument.Save(ms, DataFormats.XamlPackage);
            clonedDocumentRange.Load(ms, DataFormats.XamlPackage);
        }

        clonedDocument.ColumnWidth = originalDocument.ColumnWidth;
        clonedDocument.PageWidth = originalDocument.PageWidth;
        clonedDocument.PageHeight = originalDocument.PageHeight;
        clonedDocument.PagePadding = originalDocument.PagePadding;
        clonedDocument.LineStackingStrategy = clonedDocument.LineStackingStrategy;

        return clonedDocument;
    }
    catch (Exception)
    {               
    }

    return null;
} 

解决方案

Posting this as future reference for others that are having similar rendering issues with FlowDocument/FixedDocument/XpsDocument.

A few things to note:

  • BlockUIContainers are not cloned when you use the above method. This was not immediately obvious until I printed the logical tree out the debug window using some helper methods (these methods are posted below - they are incredibly useful).
  • You need to display the document in a viewer and briefly show it on screen. Below is the helper method that I wrote to do this for me.

ForceRenderFlowDocument

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.

Visual and Logical Tree Helpers

public static string WriteVisualTree(DependencyObject parent)
{
    if (parent == null)
        return "No Visual Tree Available. DependencyObject is null.";

    using (var stringWriter = new StringWriter())
    using (var indentedTextWriter = new IndentedTextWriter(stringWriter, "  "))
    {               
        WriteVisualTreeRecursive(indentedTextWriter, parent, 0);
        return stringWriter.ToString();
    }
}

private static void WriteVisualTreeRecursive(IndentedTextWriter writer, DependencyObject parent, int indentLevel)
{
    if (parent == null)
        return;

    int childCount = VisualTreeHelper.GetChildrenCount(parent);
    string typeName = parent.GetType().Name;
    string objName = parent.GetValue(FrameworkElement.NameProperty) as string;

    writer.Indent = indentLevel;
    writer.WriteLine(String.Format("[{0:000}] {1} ({2}) {3}", indentLevel, 
                                                              String.IsNullOrEmpty(objName) ? typeName : objName, 
                                                              typeName, childCount)
                    );

    for (int childIndex = 0; childIndex < childCount; ++childIndex)
        WriteVisualTreeRecursive(writer, VisualTreeHelper.GetChild(parent, childIndex), indentLevel + 1);
}

public static string WriteLogicalTree(DependencyObject parent)
{
    if (parent == null)
        return "No Logical Tree Available. DependencyObject is null.";

    using (var stringWriter = new StringWriter())
    using (var indentedTextWriter = new IndentedTextWriter(stringWriter, "  "))
    {
        WriteLogicalTreeRecursive(indentedTextWriter, parent, 0);
        return stringWriter.ToString();
    }
}

private static void WriteLogicalTreeRecursive(IndentedTextWriter writer, DependencyObject parent, int indentLevel)
{
    if (parent == null)
        return;

    var children = LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>();
    int childCount = children.Count();

    string typeName = parent.GetType().Name;
    string objName = parent.GetValue(FrameworkElement.NameProperty) as string;

    double actualWidth = (parent.GetValue(FrameworkElement.ActualWidthProperty) as double?).GetValueOrDefault();
    double actualHeight = (parent.GetValue(FrameworkElement.ActualHeightProperty) as double?).GetValueOrDefault();

    writer.Indent = indentLevel;
    writer.WriteLine(String.Format("[{0:000}] {1} ({2}) {3}", indentLevel,
                                                              String.IsNullOrEmpty(objName) ? typeName : objName,
                                                              typeName, 
                                                              childCount)
                    );

    foreach (object child in LogicalTreeHelper.GetChildren(parent))
    {
        if (child is DependencyObject)
            WriteLogicalTreeRecursive(writer, (DependencyObject)child, indentLevel + 1);
    }

}

Usage

#if DEBUG
    Debug.WriteLine("--- Start -------");
    Debug.WriteLine(VisualAndLogicalTreeHelper.WriteLogicalTree(document));
    Debug.WriteLine("--- End -------");
#endif

这篇关于印刷BlockUIContainer到XpsDocument /固定文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆