DocumentViewer到RichTextBox绑定错误

问题描述:

我有一个RichTextBox和DocumentViewer(放置在TabControl中)的应用程序,我想做一些类似“热预览”的东西。我已经绑定DocumentViewer.Document属性RichTextBox.DocumentDocumentViewer到RichTextBox绑定错误

绑定:

<DocumentViewer Document="{Binding Document, Converter={StaticResource FlowDocumentToPaginatorConverter}, ElementName=mainRTB, Mode=OneWay}" />

这是转换器代码:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      FlowDocument d = value as FlowDocument; 
      DocumentPaginator pagin = ((IDocumentPaginatorSource)d).DocumentPaginator; 
      FixedDocumentSequence result = null; 
      Size s = new Size(793.700787402, 1122.519685039); 
      pagin.PageSize = s; 

      using (MemoryStream ms = new MemoryStream()) 
      { 
       TextRange tr = new TextRange(d.ContentStart, d.ContentEnd); 
       tr.Save(ms, DataFormats.XamlPackage); 
       Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); 
       Uri uri = new Uri(@"memorystream://doc.xps"); 
       PackageStore.AddPackage(uri, p); 
       XpsDocument xpsDoc = new XpsDocument(p); 
       xpsDoc.Uri = uri; 
       XpsDocument.CreateXpsDocumentWriter(xpsDoc).Write(pagin); 
       result = xpsDoc.GetFixedDocumentSequence(); 
      } 
      return result; 
     } 

当我开始这个应用程序一切正常,直到我切换到与标签的DocumentViewer。应用程序粉碎,我得到这样的例外:

无法在只写模式下执行读取操作。

我在做什么错了?是否有可能使这种绑定?

错误信息确实令人困惑,原因并不明显。基本上,您关闭的MemoryStream太早,XpsDocument太早,当DocumentViewer尝试读取文档时,它不能作为只写模式(因为流已关闭)。

的解决方案是不立即关闭MemoryStream直到后查看完文档。为了达到这个目的,我写了一个XpsDocumentConverter,返回XpsReference

而且,因为你从来没有能够转换和显示单个XpsDocument你不会还没有遇到过具有相同UriPackageStore多个包的下一个问题。我在下面的实施中关注了这一点。

public static XpsDocumentReference CreateXpsDocument(FlowDocument document) 
{    
    // Do not close the memory stream as it still being used, it will be closed 
    // later when the XpsDocumentReference is Disposed. 
    MemoryStream ms = new MemoryStream(); 

    // We store the package in the PackageStore 
    Uri uri = new Uri(String.Format("pack://temp_{0}.xps/", Guid.NewGuid().ToString("N"))); 
    Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); 
    PackageStore.AddPackage(uri, pkg); 
    XpsDocument xpsDocument = new XpsDocument(pkg, CompressionOption.Normal, uri.AbsoluteUri); 

    // Need to force render the FlowDocument before pagination. 
    // HACK: This is done by *briefly* showing the document. 
    DocumentHelper.ForceRenderFlowDocument(document); 

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

    return new XpsDocumentReference(ms, xpsDocument); 
} 

public class XpsDocumentReference : IDisposable 
{ 
    private MemoryStream MemoryStream;    
    public XpsDocument XpsDocument { get; private set; } 
    public FixedDocument FixedDocument { get; private set; } 

    public XpsDocumentReference(MemoryStream ms, XpsDocument xpsDocument) 
    { 
     MemoryStream = ms; 
     XpsDocument = xpsDocument; 

     DocumentReference reference = xpsDocument.GetFixedDocumentSequence().References.FirstOrDefault(); 
     if (reference != null) 
      FixedDocument = reference.GetDocument(false); 
    } 

    public void Dispose() 
    { 
     Package pkg = PackageStore.GetPackage(XpsDocument.Uri); 
     if (pkg != null) 
     { 
      pkg.Close(); 
      PackageStore.RemovePackage(XpsDocument.Uri); 
     } 

     if (MemoryStream != null) 
     { 
      MemoryStream.Dispose(); 
      MemoryStream = null; 
     } 
    } 

} 

XpsReference实现IDisposable所以记得打电话Dispose()就可以了。

另外,一旦你解决了上述错误,你可能遇到的下一个问题就是内容不能像你期望的那样渲染。这是由于你需要克隆FlowDocument而导致的,它没有经过全面的测量并安排布局通过。阅读 Printing BlockUIContainer to XpsDocument/FixedDocument关于如何解决这个问题。

+0

按承诺的代码示例更新。 – Dennis 2012-03-12 09:36:44

+0

如果你正在寻找'ForceRenderFlowDocument'背后的魔法,它可以在这个*答案中找到。 http://*.com/questions/9447338/printing-blockuicontainer-to-xpsdocument-fixeddocument – Dennis 2012-03-14 13:22:31