代码之家  ›  专栏  ›  技术社区  ›  Tim Erickson

如何让WPF的DocumentViewer在源XPS文档上释放其文件锁定?

  •  9
  • Tim Erickson  · 技术社区  · 16 年前

    在wpf documentviewer中显示xps文件并关闭documentviewer实例后,xps文件被锁定,我无法删除它。我需要释放xps文件上的锁,以便删除该文件,使用相同名称编写另一个文件,并可以选择在新的documentviewer实例中显示该新的xps文件。我需要在同一个应用程序实例中这样做-而不必关闭应用程序(这是一个打印预览场景)。

    换句话说,我如何在不在“file.delete(tempxpsfile);”语句中引发异常的情况下运行以下代码?

    var tempXpsFile = @"c:\path\to\Temporary.xps";
    
    var previewWindow = new Window();
    var docViewer = new DocumentViewer();
    previewWindow.Content = docViewer;
    
    GenerateXpsFile(tempXpsFile);
    
    var xpsDocument = new XpsDocument(tempXpsFile);
    
    previewWindow.ShowDialog();
    
    File.Delete(tempXpsFile);  //this will throw an exception due to a file lock on tempXpsFile
    
    GenerateXpsFile(tempXpsFile); //assume this generates a different file
    //otherwise the scenario doesn't make sense as we could just skip the above delete
    //and this statement and re-use the same file
    
    previewWindow = new Window();
    docViewer = new DocumentViewer();
    previewWindow.Content = docViewer;
    
    previewWindow.ShowDialog();
    

    关闭应用程序会释放文件锁定,如中所述 WPF DocumentViewer doesn't release the XPS file 但在这种情况下,这不是一个选项。

    2 回复  |  直到 11 年前
        1
  •  14
  •   Tim Erickson    11 年前

    您需要关闭分配给查看器的xpsdocument从中打开的system.io.packaging.package。此外,如果希望能够在同一应用程序会话中再次打开同一文件,则必须从PackageStore中删除该包。关闭包将释放文件锁并允许您删除该文件,但在从包存储中删除包之前,您将无法重新打开同一个文件(或者更准确地说,同一位置的任何同名文件,即使其内容不同)。

    在问题代码的上下文中,在第一个previewWindow.showDialog()之后;在文件.delete(tempxpsfile)之前插入以下内容;

    //Get the Uri from which the system opened the XpsPackage and so your XpsDocument
    var myXpsUri = xpsDocument.Uri; //should point to the same file as tempXpsFile
    
    //Get the XpsPackage itself
    var theXpsPackage = System.IO.Packaging.PackageStore.GetPackage(myXpsUri);
    
    //THIS IS THE KEY!!!! close it and make it let go of it's file locks
    theXpsPackage.Close();
    
    //if you don't remove the package from the PackageStore, you won't be able to
    //re-open the same file again later (due to System.IO.Packaging's Package store/caching
    //rather than because of any file locks)
    System.IO.Packaging.PackageStore.RemovePackage(myXpsUri);
    

    因此,问题中出现的固定代码段变为:

    var tempXpsFile = @"c:\path\to\Temporary.xps";
    
    var previewWindow = new Window();
    var docViewer = new DocumentViewer();
    previewWindow.Content = docViewer;
    
    GenerateXpsFile(tempXpsFile);
    
    var xpsDocument = new XpsDocument(tempXpsFile);
    
    previewWindow.ShowDialog();
    
    //BEGIN NEW CODE
    var myXpsUri = xpsDocument.Uri; //should point to the same file as tempXpsFile
    var theXpsPackage = System.IO.Packaging.PackageStore.GetPackage(myXpsUri);
    theXpsPackage.Close();
    System.IO.Packaging.PackageStore.RemovePackage(myXpsUri);
    //END NEW CODE
    
    File.Delete(tempXpsFile);  //this will succeed now
    
    GenerateXpsFile(tempXpsFile);
    
    previewWindow = new Window();
    docViewer = new DocumentViewer();
    previewWindow.Content = docViewer;
    
    previewWindow.ShowDialog();
    

    是的,我知道我没有用一个包打开xpsdocument,.net在幕后为我“做”了,忘了自己清理。

        2
  •  5
  •   rationull    13 年前

    不知道这个问题最初是针对.NET的哪个版本提出的,或者它是否在3.x和4.x之间发生了变化,但是从对.NET 4.0的一些调查来看,解决方案可能比这个简单得多。

    xpsdocument实现IDisposable,表示使用后需要dispose()'d。问题在于,IDisposable.Dispose()的实现方式是隐藏的,因此您不能直接调用它。您需要改为调用close()。使用DotPeek分析xpsdocument.dispose():

    • xpsdocument.close()调用xpsdocument.dispose()。
    • xpsdocument.dispose()调用xpsmanager.close()。
    • xpsmanager.close()调用xpsmanager.removePackageReference()。
    • xpsmanager.removePackageReference()调用packageStore.removePackage()和package.close()。

    因此,除非我遗漏了一些东西,否则只需关闭xpsdocument(无论如何您都应该这样做)就可以获得相同的结果,而不必深入挖掘xpsdocument应该处理的内部包管理内容。

    推荐文章