9

在 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 中所述,不会释放 XPS 文件,但在这种情况下这不是一个选项。

4

2 回答 2

14

您需要关闭打开分配给查看器的 XpsDocument 的 System.IO.Packaging.Package。此外,如果您希望能够在同一个应用程序会话中再次打开同一个文件,则必须从 PackageStore 中删除该包。关闭包将释放文件锁定并允许您删除文件,但您将无法重新打开同一个文件(或者更准确地说,即使它有相同名称的同一位置的任何文件不同的内容),直到您从 PackageStore 中删除该包。

在问题中代码的上下文中,在第一个 previewWindow.ShowDialog(); 之后插入以下内容 在 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);

所以问题中呈现的固定代码段变为:

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 在幕后“为”我做了它并且忘记了自行清理。

于 2009-09-18T04:34:26.240 回答
6

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

XpsDocument 实现 IDisposable,表示使用后需要 Dispose()。皱纹是 IDisposable.Dispose() 的实现方式是隐藏的,因此您不能直接调用它。您需要改为调用 Close() 。使用dotPeek分析XpsDocument.Dispose():

  • XpsDocument.Close() 调用 XpsDocument.Dispose()
  • XpsDocument.Dispose() 调用 XpsManager.Close()
  • XpsManager.Close() 调用 XpsManager.RemovePackageReference()
  • XpsManager.RemovePackageReference() 调用 PackageStore.RemovePackage() 和 Package.Close()

因此,除非我遗漏了某些东西,否则只需 Close() 对 XpsDocument(无论如何您都应该这样做)应该达到相同的结果,而不必深入研究 XpsDocument 应该处理的内部包管理内容。

于 2013-06-06T21:54:06.463 回答