How do I get WPF's DocumentViewer to release its file lock on the source XPS Document?

我是研究僧i 提交于 2019-12-03 12:40:49

You need to close the System.IO.Packaging.Package from which the XpsDocument assigned to the viewer was opened. Further, if you want to be able to open the same file again within the same application session, you will have to remove the Package from the PackageStore. Closing the Package will release the file lock and allow you to delete the file, but you will not then be able to re-open that same file (or, more precisely, any file at that same location by the same name even if it has different content) until you remove the Package from the PackageStore.

In the context of the code in the question, insert the following after the first previewWindow.ShowDialog(); before the File.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);

So the fixed code segment presented in the question becomes:

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

Yes, I know I didn't open the XpsDocument with a Package - .NET did it "for" me behind the scenes and forgets to clean up after itself.

Not sure what version of .Net this question was originally asked with respect to, or whether this might've changed between 3.x and 4.x, but from some investigation against .Net 4.0 it looks like the solution might be quite a bit simpler than this.

XpsDocument implement IDisposable, indicating it needs to be Dispose()'d after use. The wrinkle is that IDisposable.Dispose() is implemented such that it's hidden so you can't call it directly. You need to call Close() instead. Using dotPeek to analyze XpsDocument.Dispose():

  • XpsDocument.Close() calls XpsDocument.Dispose()
  • XpsDocument.Dispose() calls XpsManager.Close()
  • XpsManager.Close() calls XpsManager.RemovePackageReference()
  • XpsManager.RemovePackageReference() calls PackageStore.RemovePackage() and Package.Close()

So unless I'm missing something, just Close()ing the XpsDocument (which you're supposed to do anyway) should achieve the same result without having to dig into the internal package management stuff that XpsDocument should be handling.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!